Coverage for python/lsst/analysis/drp/dataSelectors.py: 51%
154 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-16 09:35 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-16 09:35 +0000
1__all__ = ("FlagSelector", "PsfFlagSelector", "BaseSNRSelector", "SnSelector",
2 "StarIdentifier", "GalaxyIdentifier", "UnknownIdentifier",
3 "VisitPlotFlagSelector", "CoaddPlotFlagSelector")
5from abc import abstractmethod
6from lsst.pex.config import ListField, Field
7from lsst.pipe.tasks.dataFrameActions import DataFrameAction
8import numpy as np
11class FlagSelector(DataFrameAction):
12 """The base flag selector to use to select valid sources for QA"""
14 selectWhenFalse = ListField(doc="Names of the flag columns to select on when False",
15 dtype=str,
16 optional=False,
17 default=[])
19 selectWhenTrue = ListField(doc="Names of the flag columns to select on when True",
20 dtype=str,
21 optional=False,
22 default=[])
24 @property
25 def columns(self):
26 allCols = list(self.selectWhenFalse) + list(self.selectWhenTrue)
27 yield from allCols
29 def __call__(self, df, **kwargs):
30 """Select on the given flags
32 Parameters
33 ----------
34 df : `pandas.core.frame.DataFrame`
36 Returns
37 -------
38 result : `numpy.ndarray`
39 A mask of the objects that satisfy the given
40 flag cuts.
42 Notes
43 -----
44 Uses the columns in selectWhenFalse and
45 selectWhenTrue to decide which columns to
46 select on in each circumstance.
47 """
48 result = np.ones(len(df), dtype=bool)
49 for flag in self.selectWhenFalse:
50 result &= (np.array(df[flag]) == 0)
51 for flag in self.selectWhenTrue:
52 result &= (np.array(df[flag]) == 1)
53 return result
56class PsfFlagSelector(FlagSelector):
57 """Remove sources with bad flags set for PSF measurements """
59 bands = ListField(doc="The bands to apply the flags in",
60 dtype=str,
61 default=["g", "r", "i", "z", "y"])
63 @property
64 def columns(self):
65 filterColumns = ["xy_flag", "detect_isPatchInner", "detect_isDebelendedSource"]
66 flagCols = ["psfFlux_flag", "psfFlux_flag_apCorr", "psfFlux_flag_edge",
67 "psfFlux_flag_noGoodPixels"]
68 filterColumns += [band + "_" + flag if len(band) > 0 else band + flag
69 for flag in flagCols for band in self.bands]
70 yield from filterColumns
72 def __call__(self, df, **kwargs):
73 """Make a mask of objects with bad PSF flags
75 Parameters
76 ----------
77 df : `pandas.core.frame.DataFrame`
79 Returns
80 -------
81 result : `numpy.ndarray`
82 A mask of the objects that satisfy the given
83 flag cuts.
85 Notes
86 -----
87 Uses the PSF flags and some general quality
88 control flags to make a mask of the data that
89 satisfies these criteria.
90 """
92 result = None
93 flagCols = ["psfFlux_flag", "pixelFlags_saturatedCenter", "extendedness_flag"]
94 filterColumns = ["xy_flag"]
95 filterColumns += [band + "_" + flag if len(band) > 0 else band + flag
96 for flag in flagCols for band in self.bands]
97 for flag in filterColumns:
98 selected = (df[flag].values == 0)
99 if result is None:
100 result = selected
101 else:
102 result &= selected
103 result &= (df["detect_isPatchInner"] == 1)
104 result &= (df["detect_isDeblendedSource"] == 1)
105 return result
108class BaseSNRSelector(DataFrameAction):
109 """Selects sources that have a S/N greater than the
110 given threshold"""
112 fluxField = Field(doc="Flux field to use in SNR calculation", dtype=str,
113 default="psfFlux", optional=False)
114 errField = Field(doc="Flux err field to use in SNR calculation", dtype=str,
115 default="psfFluxErr", optional=False)
116 threshold = Field(doc="The signal to noise threshold to select sources",
117 dtype=float,
118 optional=False)
119 band = Field(doc="The band to make the selection in.",
120 default="i",
121 dtype=str)
123 @property
124 def columns(self):
125 if len(self.band) > 0:
126 band = self.band + "_"
127 else:
128 band = self.band
129 return (band + self.fluxField, band + self.errField)
132class SnSelector(DataFrameAction):
133 """Selects points that have S/N > threshold in the given flux type"""
134 fluxType = Field(doc="Flux type to calculate the S/N in.",
135 dtype=str,
136 default="psfFlux")
137 threshold = Field(doc="The S/N threshold to remove sources with.",
138 dtype=float,
139 default=100.0)
140 bands = ListField(doc="The bands to apply the signal to noise cut in.",
141 dtype=str,
142 default=["i"])
144 @property
145 def columns(self):
146 cols = []
147 for band in self.bands:
148 if len(band) > 0:
149 band = band + "_"
150 cols += [band + self.fluxType, band + self.fluxType + "Err"]
152 return cols
154 def __call__(self, df):
155 """Makes a mask of objects that have S/N greater than
156 self.threshold in self.fluxType
158 Parameters
159 ----------
160 df : `pandas.core.frame.DataFrame`
162 Returns
163 -------
164 result : `numpy.ndarray`
165 A mask of the objects that satisfy the given
166 S/N cut.
167 """
169 mask = np.ones(len(df), dtype=bool)
170 for band in self.bands:
171 if len(band) > 0:
172 band = band + "_"
173 mask &= ((df[band + self.fluxType] / df[band + self.fluxType + "Err"]) > self.threshold)
175 return mask
178def format_extendedness(prefix):
179 return "refExtendedness" if prefix == "ref" else (
180 f"{prefix}{'_' if len(prefix) > 0 else ''}extendedness")
183class ExtendedIdentifier(DataFrameAction):
184 band = Field(doc="The band the object is to be classified as a star in.",
185 default="i",
186 dtype=str)
188 @property
189 def columns(self):
190 return [self.extendedness]
192 @property
193 def extendedness(self):
194 return format_extendedness(self.band)
196 # TODO: Add classmethod in py3.9
197 @property
198 @abstractmethod
199 def sourceType(self):
200 raise NotImplementedError("This method should be overloaded in subclasses")
202 @abstractmethod
203 def identified(self, df):
204 raise NotImplementedError("This method should be overloaded in subclasses")
206 def __call__(self, df, **kwargs):
207 """Identifies sources classified as stars
209 Parameters
210 ----------
211 df : `pandas.core.frame.DataFrame`
213 Returns
214 -------
215 result : `numpy.ndarray`
216 An array with the objects that are classified as
217 stars marked with a 1.
218 """
219 sourceType = np.zeros(len(df))
220 sourceType[self.identified(df)] = self.sourceType
221 return sourceType
224class StarIdentifier(ExtendedIdentifier):
225 """Identifies stars from the dataFrame and marks them as a 1
226 in the added sourceType column"""
227 extendedness_maximum = Field(doc="Maximum extendedness to qualify as unresolved, inclusive.",
228 default=0.5,
229 dtype=float)
231 @property
232 @abstractmethod
233 def sourceType(self):
234 return 1
236 @abstractmethod
237 def identified(self, df):
238 extendedness = df[self.extendedness]
239 return (extendedness >= 0) & (extendedness < self.extendedness_maximum)
242class GalaxyIdentifier(ExtendedIdentifier):
243 """Identifies galaxies from the dataFrame and marks them as a 2
244 in the added sourceType column"""
245 extendedness_minimum = Field(doc="Minimum extendedness to qualify as resolved, not inclusive.",
246 default=0.5,
247 dtype=float)
249 @property
250 @abstractmethod
251 def sourceType(self):
252 return 2
254 @abstractmethod
255 def identified(self, df):
256 extendedness = df[self.extendedness]
257 return (extendedness > self.extendedness_minimum) & (extendedness <= 1)
260class UnknownIdentifier(ExtendedIdentifier):
261 """Identifies un classified objects from the dataFrame and marks them as a
262 9 in the added sourceType column"""
264 @property
265 @abstractmethod
266 def sourceType(self):
267 return 9
269 @abstractmethod
270 def identified(self, df):
271 return df[self.extendedness] == 9
274class VisitPlotFlagSelector(DataFrameAction):
276 @property
277 def columns(self):
278 flagCols = ["psfFlux_flag", "pixelFlags_saturatedCenter", "extendedness_flag", "centroid_flag",
279 "sky_source"]
280 yield from flagCols
282 def __call__(self, df, **kwargs):
283 """The flags to use for selecting sources for visit QA
285 Parameters
286 ----------
287 df : `pandas.core.frame.DataFrame`
289 Returns
290 -------
291 result : `numpy.ndarray`
292 A mask of the objects that satisfy the given
293 flag cuts.
295 Notes
296 -----
297 These flags are taken from pipe_analysis and are considered to
298 be the standard flags for general QA plots. Some of the plots
299 will require a different set of flags, or additional ones on
300 top of the ones specified here. These should be specifed in
301 an additional selector rather than adding to this one.
302 """
304 result = None
305 flagCols = ["psfFlux_flag", "pixelFlags_saturatedCenter", "extendedness_flag", "centroid_flag",
306 "sky_source"]
307 for flag in flagCols:
308 selected = (df[flag].values == 0)
309 if result is None:
310 result = selected
311 else:
312 result &= selected
313 return result
316class CoaddPlotFlagSelector(DataFrameAction):
317 """The flags to use for selecting sources for coadd QA
319 Parameters
320 ----------
321 df : `pandas.core.frame.DataFrame`
323 Returns
324 -------
325 result : `numpy.ndarray`
326 A mask of the objects that satisfy the given
327 flag cuts.
329 Notes
330 -----
331 These flags are taken from pipe_analysis and are considered to
332 be the standard flags for general QA plots. Some of the plots
333 will require a different set of flags, or additional ones on
334 top of the ones specified here. These should be specifed in
335 an additional selector rather than adding to this one.
336 """
338 bands = ListField(doc="The bands to apply the flags in",
339 dtype=str,
340 default=["g", "r", "i", "z", "y"])
342 @property
343 def columns(self):
344 flagCols = ["psfFlux_flag", "pixelFlags_saturatedCenter", "extendedness_flag"]
345 filterColumns = ["xy_flag", "detect_isPatchInner", "detect_isDeblendedSource", "sky_object"]
346 filterColumns += [band + "_" + flag if len(band) > 0 else band + flag
347 for flag in flagCols for band in self.bands]
348 yield from filterColumns
350 def __call__(self, df, **kwargs):
351 """The flags to use for selecting sources for coadd QA
353 Parameters
354 ----------
355 df : `pandas.core.frame.DataFrame`
357 Returns
358 -------
359 result : `numpy.ndarray`
360 A mask of the objects that satisfy the given
361 flag cuts.
363 Notes
364 -----
365 These flags are taken from pipe_analysis and are considered to
366 be the standard flags for general QA plots. Some of the plots
367 will require a different set of flags, or additional ones on
368 top of the ones specified here. These should be specifed in
369 an additional selector rather than adding to this one.
370 """
372 result = None
373 flagCols = ["psfFlux_flag", "pixelFlags_saturatedCenter", "extendedness_flag"]
374 filterColumns = ["xy_flag", "sky_object"]
375 filterColumns += [band + "_" + flag if len(band) > 0 else band + flag
376 for flag in flagCols for band in self.bands]
377 for flag in filterColumns:
378 selected = (df[flag].values == 0)
379 if result is None:
380 result = selected
381 else:
382 result &= selected
383 result &= (df["detect_isPatchInner"] == 1)
384 result &= (df["detect_isDeblendedSource"] == 1)
385 return result