Coverage for python / lsst / meas / extensions / multiprofit / catalog_actions.py: 17%
54 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 08:56 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 08:56 +0000
1# This file is part of meas_extensions_multiprofit.
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__ = (
23 "CatalogAction",
24 "MergeMultibandFluxes",
25)
27from collections import defaultdict
29import astropy.table
30from lsst.multiprofit.fitting.fit_catalog import CatalogFitterConfig
31import lsst.pex.config as pexConfig
32from lsst.pex.config.configurableActions import ConfigurableAction
33import numpy as np
36class CatalogAction(ConfigurableAction):
37 """Configurable action to return a catalog."""
39 def __call__(self, data, **kwargs):
40 """Return a catalog, potentially modified in-place.
42 Parameters
43 ----------
44 data
45 A dict-like catalog.
46 **kwargs
47 Additional keyword arguments.
49 Returns
50 -------
51 data
52 The original data, modified in-place.
53 """
54 return data
57class MergeMultibandFluxes(CatalogAction):
58 """Configurable action to merge single-band flux tables into one."""
60 name_model = pexConfig.Field[str](doc="The name of the model that fluxes are measured from", default="")
62 def __call__(self, data: astropy.table.Table, **kwargs):
63 datasetType = kwargs.get("datasetType")
64 prefix_model = self.name_model + ("_" if self.name_model else "")
66 # Check if the table metadata has relevant config settings
67 if (
68 self.name_model
69 and hasattr(data, "meta")
70 and datasetType
71 and (config := data.meta.get(datasetType))
72 ):
73 config_dict = config.get("config", {})
74 prefix = config_dict.get("prefix_column", CatalogFitterConfig.prefix_column.default)
75 suffix_error = config_dict.get("suffix_error", CatalogFitterConfig.suffix_error.default)
76 column_id = config_dict.get("column_id")
77 else:
78 prefix = CatalogFitterConfig.prefix_column.default
79 suffix_error = CatalogFitterConfig.suffix_error.default
80 column_id = "id" if "id" in data.colnames else None
82 columns_rest = [] if prefix else ([column_id] if column_id else [])
83 columns_flux_band = defaultdict(list)
84 for column in data.columns:
85 if not prefix or column.startswith(prefix):
86 if column.endswith("_flux"):
87 band = column.split("_")[-2]
88 columns_flux_band[band].append(column)
89 else:
90 columns_rest.append(column)
92 columns_exclude_prefix = set(columns_rest) if prefix_model else set()
94 for band, columns_band in columns_flux_band.items():
95 column_flux = f"{band}_{prefix_model}flux"
96 column_flux_err = f"{column_flux}{suffix_error}"
97 if len(columns_band) > 1:
98 # Sum up component fluxes and make a total flux column
99 flux = np.nansum([data[column] for column in columns_band], axis=0)
100 data[column_flux] = flux
102 columns_band_err = [f"{column}{suffix_error}" for column in columns_band]
103 errors = [data[column] ** 2 for column in columns_band_err if column in data.columns]
104 if errors:
105 flux_err = np.sqrt(np.nansum(errors, axis=0))
106 flux_err[flux_err == 0] = np.nan
107 data[column_flux_err] = flux_err
108 columns_exclude_prefix.add(column_flux_err)
109 else:
110 data.rename_columns(
111 (columns_band[0], f"{columns_band[0]}{suffix_error}"), (column_flux, column_flux_err)
112 )
114 columns_exclude_prefix.add(column_flux)
115 columns_exclude_prefix.add(f"{column_flux}{suffix_error}")
117 if prefix_model:
118 # Add prefixes to the column names, if needed
119 colnames = [
120 (
121 col
122 if (col in columns_exclude_prefix)
123 else (
124 f"{prefix}{prefix_model if (prefix_model != prefix) else ''}"
125 f"{col.split(prefix, 1)[1] if prefix else col}"
126 )
127 )
128 for col in data.columns
129 ]
130 if hasattr(data, "rename_columns"):
131 data.rename_columns([x for x in data.columns], colnames)
132 else:
133 data.columns = colnames
135 return data