Skip to content

Commit

Permalink
Add metrics to summarize reliability score
Browse files Browse the repository at this point in the history
  • Loading branch information
rai-harshit committed Oct 12, 2023
1 parent 9cc89a0 commit a634aab
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 1 deletion.
4 changes: 4 additions & 0 deletions pipelines/apDetectorVisitQualityCore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ tasks:
atools.numDiaSourcesAll: NumDiaSourcesAllMetric
atools.numDipoles: NumDipolesMetric
atools.numSsObjects: NumSsObjectsMetric
atools.numDiaSourcesHighReliability: NumDiaSourcesHighReliabilityMetric
atools.numDiaSourcesLowReliability: NumDiaSourcesLowReliabilityMetric
atools.numDiaSourcesNanReliability: NumDiaSourcesNanReliabilityMetric
atools.diaSourcesGoodVsBadRatio: DiaSourcesGoodVsBadRatioMetric
connections.outputName: assocDiaSrcCore
atools.simpleSky: SimpleDiaPlot
python: |
Expand Down
95 changes: 95 additions & 0 deletions python/lsst/analysis/tools/actions/scalar/scalarActions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
"MinAction",
"FracInRange",
"FracNan",
"DivideScalar",
"CountThreshold",
"CountNan"
)

import operator
from typing import cast

import numpy as np
from lsst.pex.config import ChoiceField, Field
from lsst.pex.config.configurableActions import ConfigurableActionField

from ...interfaces import KeyedData, KeyedDataSchema, Scalar, ScalarAction, Vector
from ...statistics import nansigmaMad
Expand Down Expand Up @@ -284,3 +288,94 @@ def __call__(self, data: KeyedData, **kwargs) -> Scalar:
return 100.0 * result
else:
return result


class DivideScalar(ScalarAction):
"""Calculate (A/B) for scalars."""

actionA = ConfigurableActionField[ScalarAction](doc="Action which supplies scalar A")
actionB = ConfigurableActionField[ScalarAction](doc="Action which supplies scalar B")

def getInputSchema(self) -> KeyedDataSchema:
yield from self.actionA.getInputSchema()
yield from self.actionB.getInputSchema()

def __call__(self, data: KeyedData, **kwargs) -> Scalar:
scalarA = self.actionA(data, **kwargs)
scalarB = self.actionB(data, **kwargs)
if scalarB == 0:
raise ValueError("Denominator is zero!")
return scalarA / scalarB


class CountThreshold(ScalarAction):
"""Compute the count that is above or below a specified threshold.
The operator is specified as a string, for example, "lt", "le", "ge", "gt"
for the mathematical operations <, <=, >=, >. To compute the count of
elements with values less than a given threshold, use op="le".
"""

op = ChoiceField[str](
doc="Operator name string.",
allowed={
"lt": "less than threshold",
"le": "less than or equal to threshold",
"ge": "greater than or equal to threshold",
"gt": "greater than threshold",
},
)
threshold = Field[float](doc="Threshold to apply.")
vectorKey = Field[str](doc="Name of column")
percent = Field[bool](doc="Express result as percentage", default=False)

def getInputSchema(self) -> KeyedDataSchema:
return ((self.vectorKey, Vector),)

def __call__(self, data: KeyedData, **kwargs) -> Scalar:
mask = self.getMask(**kwargs)
values = data[self.vectorKey.format(**kwargs)]
values = values[mask]
values = values[np.logical_not(np.isnan(values))]
result = cast(
Scalar,
float(np.sum(getattr(operator, self.op)(values, self.threshold))),
)
if self.percent:
return 100.0 * result
else:
return result


class CountNan(ScalarAction):
"""Compute the count of vector entries that are NaN."""

vectorKey = Field[str](doc="Name of column")
percent = Field[bool](doc="Express result as percentage", default=False)

def getInputSchema(self) -> KeyedDataSchema:
return ((self.vectorKey, Vector),)

def __call__(self, data: KeyedData, **kwargs) -> Scalar:
"""Return the fraction of rows with NaN values.
Parameters
----------
data : `KeyedData`
Returns
-------
result : `Scalar`
The count (or percentage) of rows with NaN values.
"""
mask = self.getMask(**kwargs)
values = cast(Vector, data[self.vectorKey.format(**kwargs)])[mask]
nvalues = len(values)

Check failure on line 372 in python/lsst/analysis/tools/actions/scalar/scalarActions.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

F841

local variable 'nvalues' is assigned to but never used

Check failure on line 372 in python/lsst/analysis/tools/actions/scalar/scalarActions.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

F841

local variable 'nvalues' is assigned to but never used
values = values[np.isnan(values)]
result = cast(
Scalar,
float(len(values)), # type: ignore
)
if self.percent:
return 100.0 * result
else:
return result
74 changes: 73 additions & 1 deletion python/lsst/analysis/tools/atools/diaSourceMetrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@
"NumDiaSourcesAllMetric",
"NumDiaSourcesMetric",
"NumDipolesMetric",
"NumDiaSourcesHighReliabilityMetric",
"NumDiaSourcesLowReliabilityMetric",
"NumDiaSourcesNanReliabilityMetric",
"DiaSourcesGoodVsBadRatioMetric",
)

from ..actions.scalar import CountAction
from ..actions.scalar import CountAction, CountNan, DivideScalar, CountThreshold
from ..actions.vector import FlagSelector, GoodDiaSourceSelector
from ..interfaces import AnalysisTool

Expand Down Expand Up @@ -76,3 +80,71 @@ def setDefaults(self):

# the units for the quantity (count, an astropy quantity)
self.produce.metric.units = {"numDipoles": "ct"}


class NumDiaSourcesHighReliabilityMetric(AnalysisTool):
"""Calculate the number of DIA Sources with reliability score higher than
the threshold."""

def setDefaults(self):
super().setDefaults()

# Count dia sources with reliability lower than the threshold
self.process.calculateActions.numDiaSourcesHighReliability = CountThreshold(op="gt",
threshold=0.9, vectorKey="reliability")

Check failure on line 94 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

E128

continuation line under-indented for visual indent

Check failure on line 94 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

E128

continuation line under-indented for visual indent

# The units for the quantity (count, an astropy quantity)
self.produce.metric.units = {"numDiaSourcesHighReliability": "ct"}


class NumDiaSourcesLowReliabilityMetric(AnalysisTool):
"""Calculate the number of DIA Sources with reliability score lower than
the threshold."""

def setDefaults(self):
super().setDefaults()

# Count dia sources with reliability lower than the threshold
self.process.calculateActions.numDiaSourcesLowReliability = CountThreshold(op="lt",
threshold=0.1, vectorKey="reliability")

Check failure on line 109 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

E128

continuation line under-indented for visual indent

Check failure on line 109 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

E128

continuation line under-indented for visual indent

# The units for the quantity (count, an astropy quantity)
self.produce.metric.units = {"numDiaSourcesLowReliability": "ct"}


class NumDiaSourcesNanReliabilityMetric(AnalysisTool):
"""Calculate the number of DIA Sources with Nan reliability score."""

def setDefaults(self):
super().setDefaults()

# Count dia sources with reliability lower than the threshold
self.process.calculateActions.numDiaSourcesNanReliability = CountNan(vectorKey="reliability")

# The units for the quantity (count, an astropy quantity)
self.produce.metric.units = {"numDiaSourcesNanReliability": "ct"}


class DiaSourcesGoodVsBadRatioMetric(AnalysisTool):
"""Calculate the ratio of 'good' vs 'bad' DIA Sources."""

def setDefaults(self):
super().setDefaults()

# Count dia sources with reliability higher than the threshold
self.process.buildActions.numDiaSourcesHighReliability = CountThreshold(op="gt",
threshold=0.9, vectorKey="reliability")

Check failure on line 136 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

E128

continuation line under-indented for visual indent

Check failure on line 136 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

E128

continuation line under-indented for visual indent

# Count dia sources with reliability lower than the threshold
self.process.buildActions.numDiaSourcesLowReliability = CountThreshold(op="lt",
threshold=0.1, vectorKey="reliability")

Check failure on line 140 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

E128

continuation line under-indented for visual indent

Check failure on line 140 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

E128

continuation line under-indented for visual indent

# Calculate ratio of good vs bad DIA Sources
self.process.calculateActions.DiaSourcesGoodVsBadRatio = DivideScalar(
actionA=self.process.buildActions.numDiaSourcesHighReliability,

Check failure on line 144 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

W291

trailing whitespace

Check failure on line 144 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

W291

trailing whitespace
actionB=self.process.buildActions.numDiaSourcesLowReliability
)

# The units for the quantity (dimensionless, an astropy quantity)
self.produce.metric.units = {"DiaSourcesGoodVsBadRatio": ""}

Check failure on line 150 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

W293

blank line contains whitespace

Check failure on line 150 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

W292

no newline at end of file

Check failure on line 150 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

W293

blank line contains whitespace

Check failure on line 150 in python/lsst/analysis/tools/atools/diaSourceMetrics.py

View workflow job for this annotation

GitHub Actions / call-workflow / lint

W292

no newline at end of file

0 comments on commit a634aab

Please sign in to comment.