Coverage for python/lsst/meas/algorithms/sourceSelector.py: 39%
201 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-14 20:19 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-14 20:19 +0000
1# This file is part of meas_algorithms.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22__all__ = ["BaseSourceSelectorConfig", "BaseSourceSelectorTask", "sourceSelectorRegistry",
23 "ColorLimit", "MagnitudeLimit", "SignalToNoiseLimit", "MagnitudeErrorLimit",
24 "RequireFlags", "RequireUnresolved", "RequireFiniteRaDec", "RequirePrimary",
25 "ScienceSourceSelectorConfig", "ScienceSourceSelectorTask",
26 "ReferenceSourceSelectorConfig", "ReferenceSourceSelectorTask",
27 ]
29import abc
30import numpy as np
31import astropy.units as u
32import pandas
33import astropy.table
35import lsst.pex.config as pexConfig
36import lsst.pipe.base as pipeBase
39class BaseSourceSelectorConfig(pexConfig.Config):
40 pass
43class BaseSourceSelectorTask(pipeBase.Task, metaclass=abc.ABCMeta):
44 """Base class for source selectors
46 Source selectors are classes that perform a selection on a catalog
47 object given a set of criteria or cuts. They return the selected catalog
48 and can optionally set a specified Flag field in the input catalog to
49 identify if the source was selected.
51 Register all source selectors with the sourceSelectorRegistry using:
52 sourceSelectorRegistry.register(name, class)
54 Attributes
55 ----------
56 usesMatches : `bool`
57 A boolean variable specify if the inherited source selector uses
58 matches to an external catalog, and thus requires the ``matches``
59 argument to ``run()``.
60 """
62 ConfigClass = BaseSourceSelectorConfig
63 _DefaultName = "sourceSelector"
64 usesMatches = False
66 def __init__(self, **kwargs):
67 pipeBase.Task.__init__(self, **kwargs)
69 def run(self, sourceCat, sourceSelectedField=None, matches=None, exposure=None):
70 """Select sources and return them.
72 The input catalog must be contiguous in memory.
74 Parameters
75 ----------
76 sourceCat : Various table formats
77 Catalog of sources to select from. Can be
78 `lsst.afw.table.SourceCatalog` or `pandas.DataFrame` or
79 `astropy.table.Table`,
80 sourceSelectedField : `str` or None
81 Name of flag field in sourceCat to set for selected sources.
82 If set, will modify sourceCat in-place.
83 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
84 List of matches to use for source selection.
85 If usesMatches is set in source selector this field is required.
86 If not, it is ignored.
87 exposure : `lsst.afw.image.Exposure` or None
88 The exposure the catalog was built from; used for debug display.
90 Returns
91 -------
92 struct : `lsst.pipe.base.Struct`
93 The struct contains the following data:
95 ``sourceCat``
96 The catalog of sources that were selected.
97 (may not be memory-contiguous)
98 (`lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
99 or `astropy.table.Table`)
100 ``selected``
101 Boolean array of sources that were selected, same length as
102 sourceCat.
103 (`numpy.ndarray` of `bool`)
105 Raises
106 ------
107 RuntimeError
108 Raised if ``sourceCat`` is not contiguous.
109 """
110 if hasattr(sourceCat, 'isContiguous'):
111 # Check for continuity on afwTable catalogs
112 if not sourceCat.isContiguous():
113 raise RuntimeError("Input catalogs for source selection must be contiguous.")
115 result = self.selectSources(sourceCat=sourceCat,
116 exposure=exposure,
117 matches=matches)
119 if sourceSelectedField is not None:
120 sourceCat[sourceSelectedField] = result.selected
122 return pipeBase.Struct(sourceCat=sourceCat[result.selected],
123 selected=result.selected)
125 @abc.abstractmethod
126 def selectSources(self, sourceCat, matches=None, exposure=None):
127 """Return a selection of sources selected by some criteria.
129 Parameters
130 ----------
131 sourceCat : Various table formats
132 Catalog of sources to select from. Supports
133 `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
134 or `astropy.table.Table`
135 This catalog must be contiguous in memory.
136 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
137 A list of lsst.afw.table.ReferenceMatch objects
138 exposure : `lsst.afw.image.Exposure` or None
139 The exposure the catalog was built from; used for debug display.
141 Returns
142 -------
143 struct : `lsst.pipe.base.Struct`
144 The struct contains the following data:
146 ``selected``
147 Boolean array of sources that were selected, same length as
148 sourceCat.
149 (`numpy.ndarray` of `bool`)
150 """
151 raise NotImplementedError("BaseSourceSelectorTask is abstract")
154sourceSelectorRegistry = pexConfig.makeRegistry(
155 doc="A registry of source selectors (subclasses of "
156 "BaseSourceSelectorTask)",
157)
160class BaseLimit(pexConfig.Config):
161 """Base class for selecting sources by applying a limit
163 This object can be used as a `lsst.pex.config.Config` for configuring
164 the limit, and then the `apply` method can be used to identify sources
165 in the catalog that match the configured limit.
167 This provides the `maximum` and `minimum` fields in the Config, and
168 a method to apply the limits to an array of values calculated by the
169 subclass.
170 """
171 minimum = pexConfig.Field(dtype=float, optional=True, doc="Select objects with value greater than this")
172 maximum = pexConfig.Field(dtype=float, optional=True, doc="Select objects with value less than this")
174 def apply(self, values):
175 """Apply the limits to an array of values
177 Subclasses should calculate the array of values and then
178 return the result of calling this method.
180 Parameters
181 ----------
182 values : `numpy.ndarray`
183 Array of values to which to apply limits.
185 Returns
186 -------
187 selected : `numpy.ndarray`
188 Boolean array indicating for each source whether it is selected
189 (True means selected).
190 """
191 selected = np.ones(len(values), dtype=bool)
192 with np.errstate(invalid="ignore"): # suppress NAN warnings
193 if self.minimum is not None:
194 selected &= values > self.minimum
195 if self.maximum is not None:
196 selected &= values < self.maximum
197 return selected
200class ColorLimit(BaseLimit):
201 """Select sources using a color limit
203 This object can be used as a `lsst.pex.config.Config` for configuring
204 the limit, and then the `apply` method can be used to identify sources
205 in the catalog that match the configured limit.
207 We refer to 'primary' and 'secondary' flux measurements; these are the
208 two components of the color, which is:
210 instFluxToMag(cat[primary]) - instFluxToMag(cat[secondary])
211 """
212 primary = pexConfig.Field(dtype=str, doc="Name of column with primary flux measurement")
213 secondary = pexConfig.Field(dtype=str, doc="Name of column with secondary flux measurement")
215 def apply(self, catalog):
216 """Apply the color limit to a catalog
218 Parameters
219 ----------
220 catalog : Various table formats
221 Catalog of sources to which the limit will be applied.
222 Supports `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
223 or `astropy.table.Table`
225 Returns
226 -------
227 selected : `numpy.ndarray`
228 Boolean array indicating for each source whether it is selected
229 (True means selected).
230 """
231 primary = _getFieldFromCatalog(catalog, self.primary)
232 secondary = _getFieldFromCatalog(catalog, self.secondary)
234 primary = (primary*u.nJy).to_value(u.ABmag)
235 secondary = (secondary*u.nJy).to_value(u.ABmag)
236 color = primary - secondary
237 return BaseLimit.apply(self, color)
240class FluxLimit(BaseLimit):
241 """Select sources using a flux limit
243 This object can be used as a `lsst.pex.config.Config` for configuring
244 the limit, and then the `apply` method can be used to identify sources
245 in the catalog that match the configured limit.
246 """
247 fluxField = pexConfig.Field(dtype=str, default="slot_CalibFlux_instFlux",
248 doc="Name of the source flux field to use.")
250 def apply(self, catalog):
251 """Apply the flux limits to a catalog
253 Parameters
254 ----------
255 catalog : `lsst.afw.table.SourceCatalog`
256 Catalog of sources to which the limit will be applied.
258 Returns
259 -------
260 selected : `numpy.ndarray`
261 Boolean array indicating for each source whether it is selected
262 (True means selected).
263 """
264 flagField = self.fluxField + "_flag"
265 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=True))
266 flux = _getFieldFromCatalog(catalog, self.fluxField)
268 selected &= BaseLimit.apply(self, flux)
269 return selected
272class MagnitudeLimit(BaseLimit):
273 """Select sources using a magnitude limit
275 Note that this assumes that a zero-point has already been applied and
276 the fluxes are in AB fluxes in Jansky. It is therefore principally
277 intended for reference catalogs rather than catalogs extracted from
278 science images.
280 This object can be used as a `lsst.pex.config.Config` for configuring
281 the limit, and then the `apply` method can be used to identify sources
282 in the catalog that match the configured limit.
283 """
284 fluxField = pexConfig.Field(dtype=str, default="flux",
285 doc="Name of the source flux field to use.")
287 def apply(self, catalog):
288 """Apply the magnitude limits to a catalog
290 Parameters
291 ----------
292 catalog : `lsst.afw.table.SourceCatalog`
293 Catalog of sources to which the limit will be applied.
295 Returns
296 -------
297 selected : `numpy.ndarray`
298 Boolean array indicating for each source whether it is selected
299 (True means selected).
300 """
301 flagField = self.fluxField + "_flag"
302 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=True))
303 flux = _getFieldFromCatalog(catalog, self.fluxField)
305 magnitude = (flux*u.nJy).to_value(u.ABmag)
306 selected &= BaseLimit.apply(self, magnitude)
307 return selected
310class SignalToNoiseLimit(BaseLimit):
311 """Select sources using a flux signal-to-noise limit
313 This object can be used as a `lsst.pex.config.Config` for configuring
314 the limit, and then the `apply` method can be used to identify sources
315 in the catalog that match the configured limit.
316 """
317 fluxField = pexConfig.Field(dtype=str, default="flux",
318 doc="Name of the source flux field to use.")
319 errField = pexConfig.Field(dtype=str, default="flux_err",
320 doc="Name of the source flux error field to use.")
322 def apply(self, catalog):
323 """Apply the signal-to-noise limits to a catalog
325 Parameters
326 ----------
327 catalog : `lsst.afw.table.SourceCatalog`
328 Catalog of sources to which the limit will be applied.
330 Returns
331 -------
332 selected : `numpy.ndarray`
333 Boolean array indicating for each source whether it is selected
334 (True means selected).
335 """
336 flagField = self.fluxField + "_flag"
337 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=True))
338 flux = _getFieldFromCatalog(catalog, self.fluxField)
339 err = _getFieldFromCatalog(catalog, self.errField)
341 signalToNoise = flux/err
342 selected &= BaseLimit.apply(self, signalToNoise)
343 return selected
346class MagnitudeErrorLimit(BaseLimit):
347 """Select sources using a magnitude error limit
349 Because the magnitude error is the inverse of the signal-to-noise
350 ratio, this also works to select sources by signal-to-noise when
351 you only have a magnitude.
353 This object can be used as a `lsst.pex.config.Config` for configuring
354 the limit, and then the `apply` method can be used to identify sources
355 in the catalog that match the configured limit.
356 """
357 magErrField = pexConfig.Field(dtype=str, default="mag_err",
358 doc="Name of the source flux error field to use.")
360 def apply(self, catalog):
361 """Apply the magnitude error limits to a catalog
363 Parameters
364 ----------
365 catalog : `lsst.afw.table.SourceCatalog`
366 Catalog of sources to which the limit will be applied.
368 Returns
369 -------
370 selected : `numpy.ndarray`
371 Boolean array indicating for each source whether it is selected
372 (True means selected).
373 """
374 return BaseLimit.apply(self, catalog[self.magErrField])
377class RequireFlags(pexConfig.Config):
378 """Select sources using flags
380 This object can be used as a `lsst.pex.config.Config` for configuring
381 the limit, and then the `apply` method can be used to identify sources
382 in the catalog that match the configured limit.
383 """
384 good = pexConfig.ListField(dtype=str, default=[],
385 doc="List of source flag fields that must be set for a source to be used.")
386 bad = pexConfig.ListField(dtype=str, default=[],
387 doc="List of source flag fields that must NOT be set for a source to be used.")
389 def apply(self, catalog):
390 """Apply the flag requirements to a catalog
392 Returns whether the source is selected.
394 Parameters
395 ----------
396 catalog : `lsst.afw.table.SourceCatalog`
397 Catalog of sources to which the requirements will be applied.
399 Returns
400 -------
401 selected : `numpy.ndarray`
402 Boolean array indicating for each source whether it is selected
403 (True means selected).
404 """
405 selected = np.ones(len(catalog), dtype=bool)
406 for flag in self.good:
407 selected &= catalog[flag]
408 for flag in self.bad:
409 selected &= ~catalog[flag]
410 return selected
413class RequireUnresolved(BaseLimit):
414 """Select sources using star/galaxy separation
416 This object can be used as a `lsst.pex.config.Config` for configuring
417 the limit, and then the `apply` method can be used to identify sources
418 in the catalog that match the configured limit.
419 """
420 name = pexConfig.Field(dtype=str, default="base_ClassificationExtendedness_value",
421 doc="Name of column for star/galaxy separation")
423 def setDefaults(self):
424 """Set default
426 ``base_ClassificationExtendedness_value < 0.5`` means unresolved.
427 """
428 self.maximum = 0.5
430 def apply(self, catalog):
431 """Apply the flag requirements to a catalog
433 Returns whether the source is selected.
435 Parameters
436 ----------
437 catalog : `lsst.afw.table.SourceCatalog`
438 Catalog of sources to which the requirements will be applied.
440 Returns
441 -------
442 selected : `numpy.ndarray`
443 Boolean array indicating for each source whether it is selected
444 (True means selected).
445 """
446 value = catalog[self.name]
447 return BaseLimit.apply(self, value)
450class RequireIsolated(pexConfig.Config):
451 """Select sources based on whether they are isolated
453 This object can be used as a `lsst.pex.config.Config` for configuring
454 the column names to check for "parent" and "nChild" keys.
456 Note that this should only be run on a catalog that has had the
457 deblender already run (or else deblend_nChild does not exist).
458 """
459 parentName = pexConfig.Field(dtype=str, default="parent",
460 doc="Name of column for parent")
461 nChildName = pexConfig.Field(dtype=str, default="deblend_nChild",
462 doc="Name of column for nChild")
464 def apply(self, catalog):
465 """Apply the isolation requirements to a catalog
467 Returns whether the source is selected.
469 Parameters
470 ----------
471 catalog : `lsst.afw.table.SourceCatalog`
472 Catalog of sources to which the requirements will be applied.
474 Returns
475 -------
476 selected : `numpy.ndarray`
477 Boolean array indicating for each source whether it is selected
478 (True means selected).
479 """
480 selected = ((catalog[self.parentName] == 0)
481 & (catalog[self.nChildName] == 0))
482 return selected
485class RequireFiniteRaDec(pexConfig.Config):
486 """Select sources that have finite RA and Dec sky coordinate values
488 This object can be used as a `lsst.pex.config.Config` for configuring
489 the column names to check for "coord_ra" and "coord_dec" keys.
491 This will select against objects for which either the RA or Dec coordinate
492 entries are not numpy.isfinite().
493 """
494 raColName = pexConfig.Field(dtype=str, default="coord_ra", doc="Name of column for RA coordinate")
495 decColName = pexConfig.Field(dtype=str, default="coord_dec", doc="Name of column for Dec coordinate")
497 def apply(self, catalog):
498 """Apply the sky coordinate requirements to a catalog
500 Returns whether the sources were selected.
502 Parameters
503 ----------
504 catalog : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
505 or `astropy.table.Table`
506 Catalog of sources to which the requirements will be applied.
508 Returns
509 -------
510 selected : `numpy.ndarray`
511 Boolean array indicating for each source whether it is selected
512 (True means selected).
513 """
514 selected = (np.isfinite(_getFieldFromCatalog(catalog, self.raColName))
515 & np.isfinite(_getFieldFromCatalog(catalog, self.decColName)))
516 return selected
519class RequirePrimary(pexConfig.Config):
520 """Select sources that have the detect_isPrimary flag set.
522 This object can be used as a `lsst.pex.config.Config` for configuring
523 the column names to check for "detect_isPrimary". For single frame
524 catalogs this will be True when the source is not a sky object, and is
525 either an isolated parent that is un-modeled or deblended from a parent
526 with multiple children. For meas_deblender, this is equivalent to
527 deblend_nChild=0. For coadd catalogs there is an additional constraint
528 that the source is located on the interior of a patch and tract.
529 """
530 primaryColName = pexConfig.Field(
531 dtype=str,
532 default="detect_isPrimary",
533 doc="Name of primary flag column",
534 )
536 def apply(self, catalog):
537 """Apply the primary requirements to a catalog.
539 Returns whether the sources were selected.
541 Parameters
542 ----------
543 catalog : lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
544 or `astropy.table.Table`
545 Catalog of sources to which the requirement will be applied.
547 Returns
548 -------
549 selected : `numpy.ndarray`
550 Boolean array indicating for each source whether it is selected
551 (True means selected).
552 """
553 selected = (_getFieldFromCatalog(catalog, self.primaryColName)).astype(bool)
555 return selected
558class ScienceSourceSelectorConfig(pexConfig.Config):
559 """Configuration for selecting science sources"""
560 doFluxLimit = pexConfig.Field(dtype=bool, default=False, doc="Apply flux limit?")
561 doFlags = pexConfig.Field(dtype=bool, default=False, doc="Apply flag limitation?")
562 doUnresolved = pexConfig.Field(dtype=bool, default=False, doc="Apply unresolved limitation?")
563 doSignalToNoise = pexConfig.Field(dtype=bool, default=False, doc="Apply signal-to-noise limit?")
564 doIsolated = pexConfig.Field(dtype=bool, default=False, doc="Apply isolated limitation?")
565 doRequireFiniteRaDec = pexConfig.Field(dtype=bool, default=False,
566 doc="Apply finite sky coordinate check?")
567 doRequirePrimary = pexConfig.Field(dtype=bool, default=False,
568 doc="Apply source is primary check?")
569 fluxLimit = pexConfig.ConfigField(dtype=FluxLimit, doc="Flux limit to apply")
570 flags = pexConfig.ConfigField(dtype=RequireFlags, doc="Flags to require")
571 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc="Star/galaxy separation to apply")
572 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc="Signal-to-noise limit to apply")
573 isolated = pexConfig.ConfigField(dtype=RequireIsolated, doc="Isolated criteria to apply")
574 requireFiniteRaDec = pexConfig.ConfigField(dtype=RequireFiniteRaDec,
575 doc="Finite sky coordinate criteria to apply")
576 requirePrimary = pexConfig.ConfigField(dtype=RequirePrimary,
577 doc="Primary source criteria to apply")
579 def setDefaults(self):
580 pexConfig.Config.setDefaults(self)
581 self.flags.bad = ["base_PixelFlags_flag_edge", "base_PixelFlags_flag_saturated", "base_PsfFlux_flags"]
582 self.signalToNoise.fluxField = "base_PsfFlux_instFlux"
583 self.signalToNoise.errField = "base_PsfFlux_instFluxErr"
586@pexConfig.registerConfigurable("science", sourceSelectorRegistry)
587class ScienceSourceSelectorTask(BaseSourceSelectorTask):
588 """Science source selector
590 By "science" sources, we mean sources that are on images that we
591 are processing, as opposed to sources from reference catalogs.
593 This selects (science) sources by (optionally) applying each of a
594 magnitude limit, flag requirements and star/galaxy separation.
595 """
596 ConfigClass = ScienceSourceSelectorConfig
598 def selectSources(self, sourceCat, matches=None, exposure=None):
599 """Return a selection of sources selected by specified criteria.
601 Parameters
602 ----------
603 sourceCat : `lsst.afw.table.SourceCatalog`
604 Catalog of sources to select from.
605 This catalog must be contiguous in memory.
606 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
607 Ignored in this SourceSelector.
608 exposure : `lsst.afw.image.Exposure` or None
609 The exposure the catalog was built from; used for debug display.
611 Returns
612 -------
613 struct : `lsst.pipe.base.Struct`
614 The struct contains the following data:
616 ``selected``
617 Boolean array of sources that were selected, same length as
618 sourceCat.
619 (`numpy.ndarray` of `bool`)
620 """
621 selected = np.ones(len(sourceCat), dtype=bool)
622 if self.config.doFluxLimit:
623 selected &= self.config.fluxLimit.apply(sourceCat)
624 if self.config.doFlags:
625 selected &= self.config.flags.apply(sourceCat)
626 if self.config.doUnresolved:
627 selected &= self.config.unresolved.apply(sourceCat)
628 if self.config.doSignalToNoise:
629 selected &= self.config.signalToNoise.apply(sourceCat)
630 if self.config.doIsolated:
631 selected &= self.config.isolated.apply(sourceCat)
632 if self.config.doRequireFiniteRaDec:
633 selected &= self.config.requireFiniteRaDec.apply(sourceCat)
634 if self.config.doRequirePrimary:
635 selected &= self.config.requirePrimary.apply(sourceCat)
637 self.log.info("Selected %d/%d sources", selected.sum(), len(sourceCat))
639 return pipeBase.Struct(selected=selected)
642class ReferenceSourceSelectorConfig(pexConfig.Config):
643 doMagLimit = pexConfig.Field(dtype=bool, default=False, doc="Apply magnitude limit?")
644 doFlags = pexConfig.Field(dtype=bool, default=False, doc="Apply flag limitation?")
645 doUnresolved = pexConfig.Field(dtype=bool, default=False, doc="Apply unresolved limitation?")
646 doSignalToNoise = pexConfig.Field(dtype=bool, default=False, doc="Apply signal-to-noise limit?")
647 doMagError = pexConfig.Field(dtype=bool, default=False, doc="Apply magnitude error limit?")
648 magLimit = pexConfig.ConfigField(dtype=MagnitudeLimit, doc="Magnitude limit to apply")
649 flags = pexConfig.ConfigField(dtype=RequireFlags, doc="Flags to require")
650 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc="Star/galaxy separation to apply")
651 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc="Signal-to-noise limit to apply")
652 magError = pexConfig.ConfigField(dtype=MagnitudeErrorLimit, doc="Magnitude error limit to apply")
653 colorLimits = pexConfig.ConfigDictField(keytype=str, itemtype=ColorLimit, default={},
654 doc="Color limits to apply; key is used as a label only")
657@pexConfig.registerConfigurable("references", sourceSelectorRegistry)
658class ReferenceSourceSelectorTask(BaseSourceSelectorTask):
659 """Reference source selector
661 This selects reference sources by (optionally) applying each of a
662 magnitude limit, flag requirements and color limits.
663 """
664 ConfigClass = ReferenceSourceSelectorConfig
666 def selectSources(self, sourceCat, matches=None, exposure=None):
667 """Return a selection of reference sources selected by some criteria.
669 Parameters
670 ----------
671 sourceCat : `lsst.afw.table.SourceCatalog`
672 Catalog of sources to select from.
673 This catalog must be contiguous in memory.
674 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
675 Ignored in this SourceSelector.
676 exposure : `lsst.afw.image.Exposure` or None
677 The exposure the catalog was built from; used for debug display.
679 Returns
680 -------
681 struct : `lsst.pipe.base.Struct`
682 The struct contains the following data:
684 ``selected``
685 Boolean array of sources that were selected, same length as
686 sourceCat.
687 (`numpy.ndarray` of `bool`)
688 """
689 selected = np.ones(len(sourceCat), dtype=bool)
690 if self.config.doMagLimit:
691 selected &= self.config.magLimit.apply(sourceCat)
692 if self.config.doFlags:
693 selected &= self.config.flags.apply(sourceCat)
694 if self.config.doUnresolved:
695 selected &= self.config.unresolved.apply(sourceCat)
696 if self.config.doSignalToNoise:
697 selected &= self.config.signalToNoise.apply(sourceCat)
698 if self.config.doMagError:
699 selected &= self.config.magError.apply(sourceCat)
700 for limit in self.config.colorLimits.values():
701 selected &= limit.apply(sourceCat)
703 self.log.info("Selected %d/%d references", selected.sum(), len(sourceCat))
705 return pipeBase.Struct(selected=selected)
708def _getFieldFromCatalog(catalog, field, isFlag=False):
709 """
710 Get a field from a catalog, for `lsst.afw.table` catalogs or
711 `pandas.DataFrame` or `astropy.table.Table` catalogs.
713 Parameters
714 ----------
715 catalog : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
716 or `astropy.table.Table`
717 Catalog of sources to extract field array
718 field : `str`
719 Name of field
720 isFlag : `bool`, optional
721 Is this a flag column? If it does not exist, return array
722 of False.
724 Returns
725 -------
726 array : `np.ndarray`
727 Array of field values from the catalog.
728 """
729 found = False
730 if isinstance(catalog, (pandas.DataFrame, astropy.table.Table)):
731 if field in catalog.columns:
732 found = True
733 # Sequences must be converted to numpy arrays
734 arr = np.array(catalog[field])
735 else:
736 if field in catalog.schema:
737 found = True
738 arr = catalog[field]
740 if isFlag and not found:
741 arr = np.zeros(len(catalog), dtype=bool)
742 elif not found:
743 raise KeyError(f"Could not find field {field} in catalog.")
745 return arr