LSST Applications g0f08755f38+82efc23009,g12f32b3c4e+e7bdf1200e,g1653933729+a8ce1bb630,g1a0ca8cf93+50eff2b06f,g28da252d5a+52db39f6a5,g2bbee38e9b+37c5a29d61,g2bc492864f+37c5a29d61,g2cdde0e794+c05ff076ad,g3156d2b45e+41e33cbcdc,g347aa1857d+37c5a29d61,g35bb328faa+a8ce1bb630,g3a166c0a6a+37c5a29d61,g3e281a1b8c+fb992f5633,g414038480c+7f03dfc1b0,g41af890bb2+11b950c980,g5fbc88fb19+17cd334064,g6b1c1869cb+12dd639c9a,g781aacb6e4+a8ce1bb630,g80478fca09+72e9651da0,g82479be7b0+04c31367b4,g858d7b2824+82efc23009,g9125e01d80+a8ce1bb630,g9726552aa6+8047e3811d,ga5288a1d22+e532dc0a0b,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+37c5a29d61,gcf0d15dbbd+2acd6d4d48,gd7358e8bfb+778a810b6e,gda3e153d99+82efc23009,gda6a2b7d83+2acd6d4d48,gdaeeff99f8+1711a396fd,ge2409df99d+6b12de1076,ge79ae78c31+37c5a29d61,gf0baf85859+d0a5978c5a,gf3967379c6+4954f8c433,gfb92a5be7c+82efc23009,gfec2e1e490+2aaed99252,w.2024.46
LSST Data Management Base Package
Loading...
Searching...
No Matches
match_probabilistic_task.py
Go to the documentation of this file.
1# This file is part of meas_astrom.
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/>.
21
22from typing import Dict, List, Optional, Set, Tuple
23import warnings
24
25import astropy.table
26import logging
27import lsst.afw.geom as afwGeom
28import lsst.geom as geom
29import lsst.pipe.base as pipeBase
30import lsst.utils as utils
31import numpy as np
32import pandas as pd
33
34from .matcher_probabilistic import MatchProbabilisticConfig, MatcherProbabilistic
35
36
37__all__ = ["MatchProbabilisticTask", "radec_to_xy"]
38
39
40def radec_to_xy(ra_vec, dec_vec, factor, wcs: afwGeom.SkyWcs):
41 radec_true = [
42 geom.SpherePoint(ra*factor, dec*factor, geom.degrees)
43 for ra, dec in zip(ra_vec, dec_vec)
44 ]
45 return wcs.skyToPixel(radec_true)
46
47
48class MatchProbabilisticTask(pipeBase.Task):
49 """Run MatchProbabilistic on a reference and target catalog covering the same tract."""
50
51 ConfigClass = MatchProbabilisticConfig
52 _DefaultName = "matchProbabilistic"
53
54 @staticmethod
56 catalog: astropy.table.Table | pd.DataFrame,
57 columns_true: List[str],
58 columns_false: List[str],
59 selection: Optional[np.array],
60 ) -> np.array:
61 """Apply additional boolean selection columns.
62
63 catalog : `pandas.DataFrame` | `astropy.table.Table`
64 The catalog to select from.
65 columns_true : `list` [`str`]
66 Columns that must be True for selection.
67 columns_false : `list` [`str`]
68 Columns that must be False for selection.
69 selection : `numpy.array`
70 A prior selection array. Default all true.
71
72 Returns
73 -------
74 selection : `numpy.array`
75 The final selection array.
76
77 """
78 # TODO: Remove pandas support in DM-46523
79 is_pd = isinstance(catalog, pd.DataFrame)
80 if is_pd:
81 warnings.warn("pandas usage in MatchProbabilisticTask is deprecated; it will be removed "
82 " in favour of astropy.table after release 28.0.0", category=FutureWarning)
83 select_additional = (len(columns_true) + len(columns_false)) > 0
84 if select_additional:
85 if selection is None:
86 selection = np.ones(len(catalog), dtype=bool)
87 for column in columns_true:
88 # This is intended for boolean columns, so the behaviour for non-boolean is not obvious
89 # More config options and/or using a ConfigurableActionField might be best
90 values = catalog[column] if not is_pd else catalog[column].values
91 selection &= (np.isfinite(values) & (values != 0))
92 for column in columns_false:
93 values = catalog[column] if not is_pd else catalog[column].values
94 selection &= (values == 0)
95 return selection
96
97 @property
98 def columns_in_ref(self) -> Set[str]:
99 return self.config.columns_in_ref
100
101 @property
102 def columns_in_target(self) -> Set[str]:
103 return self.config.columns_in_target
104
105 def match(
106 self,
107 catalog_ref: astropy.table.Table | pd.DataFrame,
108 catalog_target: astropy.table.Table | pd.DataFrame,
109 select_ref: np.array = None,
110 select_target: np.array = None,
111 wcs: afwGeom.SkyWcs = None,
112 logger: logging.Logger = None,
113 logging_n_rows: int = None,
114 ) -> Tuple[pd.DataFrame, pd.DataFrame, Dict[int, str]]:
115 """Match sources in a reference tract catalog with a target catalog.
116
117 Parameters
118 ----------
119 catalog_ref : `pandas.DataFrame` | `astropy.table.Table`
120 A reference catalog to match objects/sources from.
121 catalog_target : `pandas.DataFrame` | `astropy.table.Table`
122 A target catalog to match reference objects/sources to.
123 select_ref : `numpy.array`
124 A boolean array of the same length as `catalog_ref` selecting the sources that can be matched.
125 select_target : `numpy.array`
126 A boolean array of the same length as `catalog_target` selecting the sources that can be matched.
127 wcs : `lsst.afw.image.SkyWcs`
128 A coordinate system to convert catalog positions to sky coordinates. Only used if
129 `self.config.coords_ref_to_convert` is set.
130 logger : `logging.Logger`
131 A Logger for logging.
132 logging_n_rows : `int`
133 Number of matches to make before outputting incremental log message.
134
135 Returns
136 -------
137 catalog_out_ref : `pandas.DataFrame` | `astropy.table.Table`
138 Reference matched catalog with indices of target matches.
139 catalog_out_target : `pandas.DataFrame` | `astropy.table.Table`
140 Reference matched catalog with indices of target matches.
141 """
142 if logger is None:
143 logger = self.log
144
145 # TODO: Remove pandas support in DM-46523
146 is_ref_pd = isinstance(catalog_ref, pd.DataFrame)
147 is_target_pd = isinstance(catalog_target, pd.DataFrame)
148 if is_ref_pd or is_target_pd:
149 warnings.warn("pandas usage in MatchProbabilisticTask is deprecated; it will be removed "
150 " in favour of astropy.table after release 28.0.0", category=FutureWarning)
151
152 config = self.config
153
154 if config.column_ref_order is None:
155 fluxes = (
156 catalog_ref.loc[:, config.columns_ref_flux].values
157 if is_ref_pd else
158 [catalog_ref[key] for key in config.columns_ref_flux]
159 )
160 flux_tot = np.nansum(fluxes, axis=1 if is_ref_pd else 0)
161 catalog_ref["flux_total"] = flux_tot
162 if config.mag_brightest_ref != -np.inf or config.mag_faintest_ref != np.inf:
163 mag_tot = (
164 -2.5 * np.log10(flux_tot) + config.coord_format.mag_zeropoint_ref
165 )
166 select_mag = (mag_tot >= config.mag_brightest_ref) & (
167 mag_tot <= config.mag_faintest_ref
168 )
169 else:
170 select_mag = np.isfinite(flux_tot)
171 if select_ref is None:
172 select_ref = select_mag
173 else:
174 select_ref &= select_mag
175
176 with warnings.catch_warnings():
177 # We already issued a deprecation warning; no need to repeat it.
178 warnings.filterwarnings(action="ignore", category=FutureWarning)
179 select_ref = self._apply_select_bool(
180 catalog=catalog_ref,
181 columns_true=config.columns_ref_select_true,
182 columns_false=config.columns_ref_select_false,
183 selection=select_ref,
184 )
185 select_target = self._apply_select_bool(
186 catalog=catalog_target,
187 columns_true=config.columns_target_select_true,
188 columns_false=config.columns_target_select_false,
189 selection=select_target,
190 )
191
192 logger.info(
193 "Beginning MatcherProbabilistic.match with %d/%d ref sources selected vs %d/%d target",
194 np.sum(select_ref),
195 len(select_ref),
196 np.sum(select_target),
197 len(select_target),
198 )
199
200 catalog_out_ref, catalog_out_target, exceptions = self.matcher.match(
201 catalog_ref,
202 catalog_target,
203 select_ref=select_ref,
204 select_target=select_target,
205 logger=logger,
206 logging_n_rows=logging_n_rows,
207 wcs=wcs,
208 radec_to_xy_func=radec_to_xy,
209 )
210
211 return catalog_out_ref, catalog_out_target, exceptions
212
213 @utils.timer.timeMethod
214 def run(
215 self,
216 catalog_ref: astropy.table.Table | pd.DataFrame,
217 catalog_target: astropy.table.Table | pd.DataFrame,
218 wcs: afwGeom.SkyWcs = None,
219 **kwargs,
220 ) -> pipeBase.Struct:
221 """Match sources in a reference tract catalog with a target catalog.
222
223 Parameters
224 ----------
225 catalog_ref : `pandas.DataFrame` | `astropy.table.Table`
226 A reference catalog to match objects/sources from.
227 catalog_target : `pandas.DataFrame` | `astropy.table.Table`
228 A target catalog to match reference objects/sources to.
229 wcs : `lsst.afw.image.SkyWcs`
230 A coordinate system to convert catalog positions to sky coordinates.
231 Only needed if `config.coords_ref_to_convert` is used to convert
232 reference catalog sky coordinates to pixel positions.
233 kwargs : Additional keyword arguments to pass to `match`.
234
235 Returns
236 -------
237 retStruct : `lsst.pipe.base.Struct`
238 A struct with output_ref and output_target attribute containing the
239 output matched catalogs, as well as a dict
240 """
241 # TODO: Remove pandas support in DM-46523
242 is_ref_pd = isinstance(catalog_ref, pd.DataFrame)
243 is_target_pd = isinstance(catalog_target, pd.DataFrame)
244 if is_ref_pd:
245 catalog_ref.reset_index(inplace=True)
246 if is_target_pd:
247 catalog_target.reset_index(inplace=True)
248 if is_ref_pd or is_target_pd:
249 warnings.warn("pandas usage in MatchProbabilisticTask is deprecated; it will be removed "
250 " in favour of astropy.table after release 28.0.0", category=FutureWarning)
251 with warnings.catch_warnings():
252 # We already issued a deprecation warning; no need to repeat it.
253 warnings.filterwarnings(action="ignore", category=FutureWarning)
254 catalog_ref, catalog_target, exceptions = self.match(
255 catalog_ref, catalog_target, wcs=wcs, **kwargs
256 )
257
258 return pipeBase.Struct(
259 cat_output_ref=catalog_ref,
260 cat_output_target=catalog_target,
261 exceptions=exceptions,
262 )
263
264 def __init__(self, **kwargs):
265 super().__init__(**kwargs)
266 self.matcher = MatcherProbabilistic(self.config)
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
Definition SkyWcs.h:117
Point in an unspecified spherical coordinate system.
Definition SpherePoint.h:57
pipeBase.Struct run(self, astropy.table.Table|pd.DataFrame catalog_ref, astropy.table.Table|pd.DataFrame catalog_target, afwGeom.SkyWcs wcs=None, **kwargs)
np.array _apply_select_bool(astropy.table.Table|pd.DataFrame catalog, List[str] columns_true, List[str] columns_false, Optional[np.array] selection)
Tuple[pd.DataFrame, pd.DataFrame, Dict[int, str]] match(self, astropy.table.Table|pd.DataFrame catalog_ref, astropy.table.Table|pd.DataFrame catalog_target, np.array select_ref=None, np.array select_target=None, afwGeom.SkyWcs wcs=None, logging.Logger logger=None, int logging_n_rows=None)
radec_to_xy(ra_vec, dec_vec, factor, afwGeom.SkyWcs wcs)