Skip to content

Commit

Permalink
Add DiffMatchedAnalysisTask, pipeline and dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
taranu committed Oct 6, 2022
1 parent 5b7cf2a commit 7dd41ae
Show file tree
Hide file tree
Showing 10 changed files with 538 additions and 18 deletions.
74 changes: 74 additions & 0 deletions pipelines/coaddDiffMatchedQuality.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
description: |
Matched difference (measured vs reference) plots/metrics
tasks:
diff_matched_analysis:
class: lsst.analysis.tools.tasks.DiffMatchedAnalysisTask
config:
bands: ('u', 'g', 'r', 'i', 'z', 'y')
connections.outputName: diff_matched_truth_summary_objectTable_tract

# plots
plots.matchedRefCModelMagDiffPlot: MatchedRefCoaddCModelFluxPlot
plots.matchedRefCModelMagDiffPlot.applyContext: MatchedRefDiffContext

plots.matchedRefCModelFluxChiPlot: MatchedRefCoaddCModelFluxPlot
plots.matchedRefCModelFluxChiPlot.applyContext: MatchedRefChiContext

# TODO: Can this be a one liner?
plots.matchedRefPositionXDiffPlot: MatchedRefCoaddPositionPlot
# TODO: variable must be defined before applyContext; can this be enforced?
# (the resulting error if not is not very informative)
plots.matchedRefPositionXDiffPlot.variable: x
plots.matchedRefPositionXDiffPlot.applyContext: MatchedRefDiffContext

plots.matchedRefPositionXChiPlot: MatchedRefCoaddPositionPlot
plots.matchedRefPositionXChiPlot.variable: x
plots.matchedRefPositionXChiPlot.applyContext: MatchedRefChiContext

plots.matchedRefPositionYDiffPlot: MatchedRefCoaddPositionPlot
plots.matchedRefPositionYDiffPlot.variable: y
plots.matchedRefPositionYDiffPlot.applyContext: MatchedRefDiffContext

plots.matchedRefPositionYChiPlot: MatchedRefCoaddPositionPlot
plots.matchedRefPositionYChiPlot.variable: y
plots.matchedRefPositionYChiPlot.applyContext: MatchedRefChiContext

# metrics
metrics.matchedRefCModelMagDiffMetric: MatchedRefCoaddCModelFluxMetric
metrics.matchedRefCModelMagDiffMetric.applyContext: MatchedRefDiffContext

metrics.matchedRefCModelFluxChiMetric: MatchedRefCoaddCModelFluxMetric
metrics.matchedRefCModelFluxChiMetric.applyContext: MatchedRefChiContext

metrics.matchedRefPositionXDiffMetric: MatchedRefCoaddPositionMetric
metrics.matchedRefPositionXDiffMetric.variable: x
metrics.matchedRefPositionXDiffMetric.applyContext: MatchedRefChiContext

metrics.matchedRefPositionXChiMetric: MatchedRefCoaddPositionMetric
metrics.matchedRefPositionXChiMetric.variable: x
metrics.matchedRefPositionXChiMetric.applyContext: MatchedRefChiContext

metrics.matchedRefPositionYDiffMetric: MatchedRefCoaddPositionMetric
metrics.matchedRefPositionYDiffMetric.variable: y
metrics.matchedRefPositionYDiffMetric.applyContext: MatchedRefDiffContext

metrics.matchedRefPositionYChiMetric: MatchedRefCoaddPositionMetric
metrics.matchedRefPositionYChiMetric.variable: y
metrics.matchedRefPositionYChiMetric.applyContext: MatchedRefChiContext


python: |
from lsst.analysis.tools.analysisMetrics.diffMatchedMetrics import (
MatchedRefCoaddCModelFluxMetric,
MatchedRefCoaddPositionMetric,
)
from lsst.analysis.tools.analysisPlots.diffMatchedPlots import (
MatchedRefCoaddCModelFluxPlot,
MatchedRefCoaddPositionPlot,
)
from lsst.analysis.tools.contexts._contexts import (
MatchedRefDiffContext,
MatchedRefChiContext,
)
24 changes: 13 additions & 11 deletions python/lsst/analysis/tools/actions/vector/calcBinnedStats.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
class CalcBinnedStatsAction(KeyedDataAction):
key_vector = Field[str](doc="Vector on which to compute statistics")
name_prefix = Field[str](doc="Field name to append stat names to")
name_suffix = Field[str](doc="Field name to append to stat names")
selector_range = ConfigurableActionField[RangeSelector](doc="Range selector")

def getInputSchema(self, **kwargs) -> KeyedDataSchema:
Expand All @@ -58,31 +59,31 @@ def getOutputSchema(self) -> KeyedDataSchema:

@cached_property
def name_count(self):
return f"{self.name_prefix}_count"
return f"{self.name_prefix}count{self.name_suffix}"

@cached_property
def name_mask(self):
return f"{self.name_prefix}_mask"
return f"{self.name_prefix}mask{self.name_suffix}"

@cached_property
def name_median(self):
return f"{self.name_prefix}_median"
return f"{self.name_prefix}median{self.name_suffix}"

@cached_property
def name_select_maximum(self):
return f"{self.name_prefix}_select_maximum"
return f"{self.name_prefix}select_maximum{self.name_suffix}"

@cached_property
def name_select_median(self):
return f"{self.name_prefix}_select_median"
return f"{self.name_prefix}select_median{self.name_suffix}"

@cached_property
def name_select_minimum(self):
return f"{self.name_prefix}_select_minimum"
return f"{self.name_prefix}select_minimum{self.name_suffix}"

@cached_property
def name_sigmaMad(self):
return f"{self.name_prefix}_sigmaMad"
return f"{self.name_prefix}sigmaMad{self.name_suffix}"

def __call__(self, data: KeyedData, **kwargs) -> KeyedData:
results = {}
Expand All @@ -98,10 +99,11 @@ def __call__(self, data: KeyedData, **kwargs) -> KeyedData:
for name, value in action(data, **kwargs).items():
results[getattr(self, f"name_{name}")] = value

values = cast(Vector, data[self.selector_range.column][mask]) # type: ignore
results[self.name_select_maximum] = cast(Scalar, float(np.nanmax(values)))
results[self.name_select_median] = cast(Scalar, float(np.nanmedian(values)))
results[self.name_select_minimum] = cast(Scalar, float(np.nanmin(values)))
values = cast(Vector, data[self.selector_range.key][mask])
valid = np.sum(np.isfinite(values)) > 0
results[self.name_select_maximum] = cast(Scalar, float(np.nanmax(values)) if valid else np.nan)
results[self.name_select_median] = cast(Scalar, float(np.nanmedian(values)) if valid else np.nan)
results[self.name_select_minimum] = cast(Scalar, float(np.nanmin(values)) if valid else np.nan)
results["range_maximum"] = self.selector_range.maximum
results["range_minimum"] = self.selector_range.minimum

Expand Down
6 changes: 3 additions & 3 deletions python/lsst/analysis/tools/actions/vector/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,12 @@ def setDefaults(self):
class RangeSelector(VectorAction):
"""Selects rows within a range, inclusive of min/exclusive of max."""

column = Field[str](doc="Column to select from")
key = Field[str](doc="Key to select from data")
maximum = Field[float](doc="The maximum value", default=np.Inf)
minimum = Field[float](doc="The minimum value", default=np.nextafter(-np.Inf, 0.0))

def getInputSchema(self) -> KeyedDataSchema:
yield self.column, Vector
yield self.key, Vector

def __call__(self, data: KeyedData, **kwargs) -> Vector:
"""Return a mask of rows with values within the specified range.
Expand All @@ -186,7 +186,7 @@ def __call__(self, data: KeyedData, **kwargs) -> Vector:
result : `Vector`
A mask of the rows with values within the specified range.
"""
values = cast(Vector, data[self.column])
values = cast(Vector, data[self.key])
mask = (values >= self.minimum) & (values < self.maximum)

return np.array(mask)
Expand Down
141 changes: 141 additions & 0 deletions python/lsst/analysis/tools/analysisMetrics/diffMatchedMetrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# This file is part of analysis_tools.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations

__all__ = ("MatchedRefCoaddMetric", "MatchedRefCoaddCModelFluxMetric", "MatchedRefCoaddPositionMetric")

from ..actions.vector.calcBinnedStats import CalcBinnedStatsAction
from ..actions.vector.selectors import RangeSelector
from ..analysisParts.diffMatched import MatchedRefCoaddDiffMagTool, MatchedRefCoaddDiffPositionTool
from ..interfaces import AnalysisMetric


class MatchedRefCoaddMetric(AnalysisMetric):
mag_low_min: int = 15
mag_low_max: int = 27
mag_interval: int = 1

names = ("stars", "galaxies", "all")
types = ("unresolved", "resolved", "all")

def configureMetrics(self, unit: str, name_prefix: str, name_suffix: str, unit_select: str = "mag"):
"""Configure metric actions and return units.
Parameters
----------
unit : `str`
The (astropy) unit of the summary statistic metrics.
name_prefix : `str`
The prefix for the action (column) name.
name_suffix : `str`
The sufffix for the action (column) name.
unit_select : `str`
The (astropy) unit of the selection (x-axis) column. Default "mag".
Returns
-------
units : `dict` [`str`, `str`]
A dict of the unit (value) for each metric name (key)
"""
if unit_select is None:
unit_select = "mag"
units = {}
for name, name_class in zip(self.names, self.types):
name_capital = name.capitalize()
x_key = f"x{name_capital}"

for minimum in range(self.mag_low_min, self.mag_low_max + 1):
action = getattr(self.process.calculateActions, f"{name}{minimum}")
action.selector_range = RangeSelector(
key=x_key,
minimum=minimum,
maximum=minimum + self.mag_interval,
)
action.name_prefix = name_prefix.format(name_class=name_class)
action.name_suffix = name_suffix.format(minimum=minimum)

units.update(
{
action.name_median: unit,
action.name_sigmaMad: unit,
action.name_count: "count",
action.name_select_minimum: unit_select,
action.name_select_median: unit_select,
action.name_select_maximum: unit_select,
}
)
return units

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

for name in self.names:
name_capital = name.capitalize()
for minimum in range(self.mag_low_min, self.mag_low_max + 1):
setattr(
self.process.calculateActions,
f"{name}{minimum}",
CalcBinnedStatsAction(key_vector=f"y{name_capital}"),
)


class MatchedRefCoaddCModelFluxMetric(MatchedRefCoaddDiffMagTool, MatchedRefCoaddMetric):
def matchedRefDiffContext(self):
super().matchedRefDiffContext()
self.produce.units = self.configureMetrics(
unit="mmag",
name_prefix="photom_mag_cModelFlux_{name_class}_diff_",
name_suffix="_mad_ref_mag{minimum}",
)

def matchedRefChiContext(self):
super().matchedRefChiContext()
self.produce.units = self.configureMetrics(
unit="",
name_prefix="photom_mag_cModelFlux_{name_class}_chi_",
name_suffix="_mad_ref_mag{minimum}",
)

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


class MatchedRefCoaddPositionMetric(MatchedRefCoaddDiffPositionTool, MatchedRefCoaddMetric):
def matchedRefDiffContext(self):
super().matchedRefDiffContext()
self.produce.units = self.configureMetrics(
unit="pix",
name_prefix=f"astrom_{self.variable}_{{name_class}}_diff_",
name_suffix="_mad_ref_mag{minimum}",
)

def matchedRefChiContext(self):
super().matchedRefChiContext()
self.produce.units = self.configureMetrics(
unit="",
name_prefix=f"astrom_{self.variable}_{{name_class}}_diff_",
name_suffix="_mad_ref_mag{minimum}",
)

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

0 comments on commit 7dd41ae

Please sign in to comment.