3 from itertools
import product
7 import astropy.units
as u
10 from lsst.daf.butler
import DeferredDatasetHandle
11 from .parquetTable
import ParquetTable, MultilevelParquetTable
15 typeKey='functor', name=None):
16 """Initialize an object defined in a dictionary
18 The object needs to be importable as
19 f'{basePath}.{initDict[typeKey]}'
20 The positional and keyword arguments (if any) are contained in
21 "args" and "kwargs" entries in the dictionary, respectively.
22 This is used in `functors.CompositeFunctor.from_yaml` to initialize
23 a composite functor from a specification in a YAML file.
28 Dictionary describing object's initialization. Must contain
29 an entry keyed by ``typeKey`` that is the name of the object,
30 relative to ``basePath``.
32 Path relative to module in which ``initDict[typeKey]`` is defined.
34 Key of ``initDict`` that is the name of the object
35 (relative to `basePath`).
37 initDict = initDict.copy()
39 pythonType =
doImport(f
'{basePath}.{initDict.pop(typeKey)}')
41 if 'args' in initDict:
42 args = initDict.pop(
'args')
43 if isinstance(args, str):
46 element = pythonType(*args, **initDict)
47 except Exception
as e:
48 message = f
'Error in constructing functor "{name}" of type {pythonType.__name__} with args: {args}'
49 raise type(e)(message, e.args)
54 """Define and execute a calculation on a ParquetTable
56 The `__call__` method accepts either a `ParquetTable` object or a
57 `DeferredDatasetHandle`, and returns the
58 result of the calculation as a single column. Each functor defines what
59 columns are needed for the calculation, and only these columns are read
60 from the `ParquetTable`.
62 The action of `__call__` consists of two steps: first, loading the
63 necessary columns from disk into memory as a `pandas.DataFrame` object;
64 and second, performing the computation on this dataframe and returning the
68 To define a new `Functor`, a subclass must define a `_func` method,
69 that takes a `pandas.DataFrame` and returns result in a `pandas.Series`.
70 In addition, it must define the following attributes
72 * `_columns`: The columns necessary to perform the calculation
73 * `name`: A name appropriate for a figure axis label
74 * `shortname`: A name appropriate for use as a dictionary key
76 On initialization, a `Functor` should declare what band (`filt` kwarg)
77 and dataset (e.g. `'ref'`, `'meas'`, `'forced_src'`) it is intended to be
78 applied to. This enables the `_get_data` method to extract the proper
79 columns from the parquet file. If not specified, the dataset will fall back
80 on the `_defaultDataset`attribute. If band is not specified and `dataset`
81 is anything other than `'ref'`, then an error will be raised when trying to
82 perform the calculation.
84 As currently implemented, `Functor` is only set up to expect a
85 dataset of the format of the `deepCoadd_obj` dataset; that is, a
86 dataframe with a multi-level column index,
87 with the levels of the column index being `band`,
88 `dataset`, and `column`. This is defined in the `_columnLevels` attribute,
89 as well as being implicit in the role of the `filt` and `dataset` attributes
90 defined at initialization. In addition, the `_get_data` method that reads
91 the dataframe from the `ParquetTable` will return a dataframe with column
92 index levels defined by the `_dfLevels` attribute; by default, this is
95 The `_columnLevels` and `_dfLevels` attributes should generally not need to
96 be changed, unless `_func` needs columns from multiple filters or datasets
97 to do the calculation.
98 An example of this is the `lsst.pipe.tasks.functors.Color` functor, for
99 which `_dfLevels = ('band', 'column')`, and `_func` expects the dataframe
100 it gets to have those levels in the column index.
105 Filter upon which to do the calculation
108 Dataset upon which to do the calculation
109 (e.g., 'ref', 'meas', 'forced_src').
113 _defaultDataset =
'ref'
114 _columnLevels = (
'band',
'dataset',
'column')
115 _dfLevels = (
'column',)
116 _defaultNoDup =
False
118 def __init__(self, filt=None, dataset=None, noDup=None):
125 if self.
_noDup_noDup
is not None:
132 """Columns required to perform calculation
134 if not hasattr(self,
'_columns'):
135 raise NotImplementedError(
'Must define columns property or _columns attribute')
138 def _get_data_columnLevels(self, data, columnIndex=None):
139 """Gets the names of the column index levels
141 This should only be called in the context of a multilevel table.
142 The logic here is to enable this to work both with the gen2 `MultilevelParquetTable`
143 and with the gen3 `DeferredDatasetHandle`.
147 data : `MultilevelParquetTable` or `DeferredDatasetHandle`
149 columnnIndex (optional): pandas `Index` object
150 if not passed, then it is read from the `DeferredDatasetHandle`
152 if isinstance(data, DeferredDatasetHandle):
153 if columnIndex
is None:
154 columnIndex = data.get(component=
"columns")
155 if columnIndex
is not None:
156 return columnIndex.names
157 if isinstance(data, MultilevelParquetTable):
158 return data.columnLevels
160 raise TypeError(f
"Unknown type for data: {type(data)}!")
162 def _get_data_columnLevelNames(self, data, columnIndex=None):
163 """Gets the content of each of the column levels for a multilevel table
165 Similar to `_get_data_columnLevels`, this enables backward compatibility with gen2.
167 Mirrors original gen2 implementation within `pipe.tasks.parquetTable.MultilevelParquetTable`
169 if isinstance(data, DeferredDatasetHandle):
170 if columnIndex
is None:
171 columnIndex = data.get(component=
"columns")
172 if columnIndex
is not None:
173 columnLevels = columnIndex.names
175 level:
list(np.unique(np.array([c
for c
in columnIndex])[:, i]))
176 for i, level
in enumerate(columnLevels)
178 return columnLevelNames
179 if isinstance(data, MultilevelParquetTable):
180 return data.columnLevelNames
182 raise TypeError(f
"Unknown type for data: {type(data)}!")
184 def _colsFromDict(self, colDict, columnIndex=None):
185 """Converts dictionary column specficiation to a list of columns
187 This mirrors the original gen2 implementation within `pipe.tasks.parquetTable.MultilevelParquetTable`
192 for i, lev
in enumerate(columnLevels):
194 if isinstance(colDict[lev], str):
195 new_colDict[lev] = [colDict[lev]]
197 new_colDict[lev] = colDict[lev]
199 new_colDict[lev] = columnIndex.levels[i]
201 levelCols = [new_colDict[lev]
for lev
in columnLevels]
202 cols = product(*levelCols)
206 """Returns columns needed by functor from multilevel dataset
208 To access tables with multilevel column structure, the `MultilevelParquetTable`
209 or `DeferredDatasetHandle` need to be passed either a list of tuples or a
214 data : `MultilevelParquetTable` or `DeferredDatasetHandle`
216 columnIndex (optional): pandas `Index` object
217 either passed or read in from `DeferredDatasetHandle`.
220 If true, then return a list of tuples rather than the column dictionary
221 specification. This is set to `True` by `CompositeFunctor` in order to be able to
222 combine columns from the various component functors.
225 if isinstance(data, DeferredDatasetHandle)
and columnIndex
is None:
226 columnIndex = data.get(component=
"columns")
233 "ParquetTable does not have the expected column levels. "
234 f
"Got {columnLevels}; expected {self._columnLevels}."
237 columnDict = {
'column': self.
columnscolumns,
238 'dataset': self.
datasetdataset}
239 if self.
filtfilt
is None:
241 if "band" in columnLevels:
242 if self.
datasetdataset ==
"ref":
243 columnDict[
"band"] = columnLevelNames[
"band"][0]
245 raise ValueError(f
"'filt' not set for functor {self.name}"
246 f
"(dataset {self.dataset}) "
248 "contains multiple filters in column index. "
249 "Set 'filt' or set 'dataset' to 'ref'.")
251 columnDict[
'band'] = self.
filtfilt
253 if isinstance(data, MultilevelParquetTable):
254 return data._colsFromDict(columnDict)
255 elif isinstance(data, DeferredDatasetHandle):
257 return self.
_colsFromDict_colsFromDict(columnDict, columnIndex=columnIndex)
261 def _func(self, df, dropna=True):
262 raise NotImplementedError(
'Must define calculation on dataframe')
264 def _get_columnIndex(self, data):
265 """Return columnIndex
268 if isinstance(data, DeferredDatasetHandle):
269 return data.get(component=
"columns")
273 def _get_data(self, data):
274 """Retrieve dataframe necessary for calculation.
276 The data argument can be a DataFrame, a ParquetTable instance, or a gen3 DeferredDatasetHandle
278 Returns dataframe upon which `self._func` can act.
280 N.B. while passing a raw pandas `DataFrame` *should* work here, it has not been tested.
282 if isinstance(data, pd.DataFrame):
287 is_multiLevel = isinstance(data, MultilevelParquetTable)
or isinstance(columnIndex, pd.MultiIndex)
290 if isinstance(data, ParquetTable)
and not is_multiLevel:
292 df = data.toDataFrame(columns=columns)
301 if isinstance(data, MultilevelParquetTable):
303 df = data.toDataFrame(columns=columns, droplevels=
False)
304 elif isinstance(data, DeferredDatasetHandle):
306 df = data.get(parameters={
"columns": columns})
314 def _setLevels(self, df):
315 levelsToDrop = [n
for n
in df.columns.names
if n
not in self.
_dfLevels_dfLevels]
316 df.columns = df.columns.droplevel(levelsToDrop)
319 def _dropna(self, vals):
325 vals = self.
_func_func(df)
327 vals = self.
failfail(df)
329 vals = self.
_dropna_dropna(vals)
334 """Computes difference between functor called on two different ParquetTable objects
336 return self(data1, **kwargs) - self(data2, **kwargs)
339 return pd.Series(np.full(len(df), np.nan), index=df.index)
343 """Full name of functor (suitable for figure labels)
345 return NotImplementedError
349 """Short name of functor (suitable for column name/dict key)
355 """Perform multiple calculations at once on a catalog
357 The role of a `CompositeFunctor` is to group together computations from
358 multiple functors. Instead of returning `pandas.Series` a
359 `CompositeFunctor` returns a `pandas.Dataframe`, with the column names
360 being the keys of `funcDict`.
362 The `columns` attribute of a `CompositeFunctor` is the union of all columns
363 in all the component functors.
365 A `CompositeFunctor` does not use a `_func` method itself; rather,
366 when a `CompositeFunctor` is called, all its columns are loaded
367 at once, and the resulting dataframe is passed to the `_func` method of each component
368 functor. This has the advantage of only doing I/O (reading from parquet file) once,
369 and works because each individual `_func` method of each component functor does not
370 care if there are *extra* columns in the dataframe being passed; only that it must contain
371 *at least* the `columns` it expects.
373 An important and useful class method is `from_yaml`, which takes as argument the path to a YAML
374 file specifying a collection of functors.
378 funcs : `dict` or `list`
379 Dictionary or list of functors. If a list, then it will be converted
380 into a dictonary according to the `.shortname` attribute of each functor.
387 if type(funcs) == dict:
390 self.
funcDictfuncDict = {f.shortname: f
for f
in funcs}
392 self.
_filt_filt =
None
398 return self.
_filt_filt
405 self.
_filt_filt = filt
408 if isinstance(new, dict):
410 elif isinstance(new, CompositeFunctor):
413 raise TypeError(
'Can only update with dictionary or CompositeFunctor.')
421 return list(
set([x
for y
in [f.columns
for f
in self.
funcDictfuncDict.values()]
for x
in y]))
430 f.multilevelColumns(data, returnTuple=
True, **kwargs)
for f
in self.
funcDictfuncDict.values()
438 """Apply the functor to the data table
442 data : `lsst.daf.butler.DeferredDatasetHandle`,
443 `lsst.pipe.tasks.parquetTable.MultilevelParquetTable`,
444 `lsst.pipe.tasks.parquetTable.ParquetTable`,
445 or `pandas.DataFrame`.
446 The table or a pointer to a table on disk from which columns can
452 is_multiLevel = isinstance(data, MultilevelParquetTable)
or isinstance(columnIndex, pd.MultiIndex)
458 if isinstance(data, MultilevelParquetTable):
460 df = data.toDataFrame(columns=columns, droplevels=
False)
461 elif isinstance(data, DeferredDatasetHandle):
463 df = data.get(parameters={
"columns": columns})
468 subdf = f._setLevels(
469 df[f.multilevelColumns(data, returnTuple=
True, columnIndex=columnIndex)]
471 valDict[k] = f._func(subdf)
473 valDict[k] = f.fail(subdf)
476 if isinstance(data, DeferredDatasetHandle):
479 elif isinstance(data, pd.DataFrame):
486 valDict = {k: f._func(df)
for k, f
in self.
funcDictfuncDict.
items()}
489 valDf = pd.concat(valDict, axis=1)
491 print([(k,
type(v))
for k, v
in valDict.items()])
494 if kwargs.get(
'dropna',
False):
495 valDf = valDf.dropna(how=
'any')
501 if renameRules
is None:
503 for old, new
in renameRules:
504 if col.startswith(old):
505 col = col.replace(old, new)
510 with open(filename)
as f:
511 translationDefinition = yaml.safe_load(f)
513 return cls.
from_yamlfrom_yaml(translationDefinition, **kwargs)
518 for func, val
in translationDefinition[
'funcs'].
items():
521 if 'flag_rename_rules' in translationDefinition:
522 renameRules = translationDefinition[
'flag_rename_rules']
526 if 'refFlags' in translationDefinition:
527 for flag
in translationDefinition[
'refFlags']:
528 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'ref')
530 if 'flags' in translationDefinition:
531 for flag
in translationDefinition[
'flags']:
532 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'meas')
534 return cls(funcs, **kwargs)
538 """Evaluate an expression on a DataFrame, knowing what the 'mag' function means
540 Builds on `pandas.DataFrame.eval`, which parses and executes math on dataframes.
544 df : pandas.DataFrame
545 Dataframe on which to evaluate expression.
551 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>)/log(10)', expr)
552 val = df.eval(expr_new, truediv=
True)
554 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>_instFlux)/log(10)', expr)
555 val = df.eval(expr_new, truediv=
True)
560 """Arbitrary computation on a catalog
562 Column names (and thus the columns to be loaded from catalog) are found
563 by finding all words and trying to ignore all "math-y" words.
568 Expression to evaluate, to be parsed and executed by `mag_aware_eval`.
570 _ignore_words = (
'mag',
'sin',
'cos',
'exp',
'log',
'sqrt')
582 flux_cols = re.findall(
r'mag\(\s*(\w+)\s*\)', self.
exprexpr)
584 cols = [c
for c
in re.findall(
r'[a-zA-Z_]+', self.
exprexpr)
if c
not in self.
_ignore_words_ignore_words]
587 if not re.search(
'_instFlux$', c):
588 cols.append(f
'{c}_instFlux')
593 return list(
set([c
for c
in cols
if c
not in not_a_col]))
600 """Get column with specified name
616 return df[self.
colcol]
620 """Return the value of the index for each object
623 columns = [
'coord_ra']
624 _defaultDataset =
'ref'
628 return pd.Series(df.index, index=df.index)
633 _allow_difference =
False
637 return pd.Series(df.index, index=df.index)
641 col =
'base_Footprint_nPix'
645 """Base class for coordinate column, in degrees
654 output = df[self.
colcol] * 180 / np.pi
if self.
_radians_radians
else df[self.
colcol]
659 """Right Ascension, in degrees
665 super().
__init__(
'coord_ra', **kwargs)
668 return super().
__call__(catalog, **kwargs)
672 """Declination, in degrees
678 super().
__init__(
'coord_dec', **kwargs)
681 return super().
__call__(catalog, **kwargs)
685 if not col.endswith(
'_instFlux'):
691 if not col.endswith(
'_instFluxErr'):
692 col +=
'_instFluxErr'
697 """Compute calibrated magnitude
699 Takes a `calib` argument, which returns the flux at mag=0
700 as `calib.getFluxMag0()`. If not provided, then the default
701 `fluxMag0` is 63095734448.0194, which is default for HSC.
702 This default should be removed in DM-21955
704 This calculation hides warnings about invalid values and dividing by zero.
706 As for all functors, a `dataset` and `filt` kwarg should be provided upon
707 initialization. Unlike the default `Functor`, however, the default dataset
708 for a `Mag` is `'meas'`, rather than `'ref'`.
713 Name of flux column from which to compute magnitude. Can be parseable
714 by `lsst.pipe.tasks.functors.fluxName` function---that is, you can pass
715 `'modelfit_CModel'` instead of `'modelfit_CModel_instFlux'`) and it will
717 calib : `lsst.afw.image.calib.Calib` (optional)
718 Object that knows zero point.
720 _defaultDataset =
'meas'
725 if calib
is not None:
729 self.
fluxMag0fluxMag0 = 63095734448.0194
738 with np.warnings.catch_warnings():
739 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
740 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
741 return -2.5*np.log10(df[self.
colcol] / self.
fluxMag0fluxMag0)
745 return f
'mag_{self.col}'
749 """Compute calibrated magnitude uncertainty
751 Takes the same `calib` object as `lsst.pipe.tasks.functors.Mag`.
756 calib : `lsst.afw.image.calib.Calib` (optional)
757 Object that knows zero point.
762 if self.
calibcalib
is not None:
769 return [self.
colcol, self.
colcol +
'Err']
772 with np.warnings.catch_warnings():
773 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
774 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
776 x = df[fluxErrCol] / df[fluxCol]
778 magErr = (2.5 / np.log(10.)) * np.sqrt(x*x + y*y)
783 return super().name +
'_err'
791 return (df[self.
colcol] / self.
fluxMag0fluxMag0) * 1e9
795 _defaultDataset =
'meas'
797 """Functor to calculate magnitude difference"""
806 return [self.
col1col1, self.
col2col2]
809 with np.warnings.catch_warnings():
810 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
811 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
812 return -2.5*np.log10(df[self.
col1col1]/df[self.
col2col2])
816 return f
'(mag_{self.col1} - mag_{self.col2})'
820 return f
'magDiff_{self.col1}_{self.col2}'
824 """Compute the color between two filters
826 Computes color by initializing two different `Mag`
827 functors based on the `col` and filters provided, and
828 then returning the difference.
830 This is enabled by the `_func` expecting a dataframe with a
831 multilevel column index, with both `'band'` and `'column'`,
832 instead of just `'column'`, which is the `Functor` default.
833 This is controlled by the `_dfLevels` attribute.
835 Also of note, the default dataset for `Color` is `forced_src'`,
836 whereas for `Mag` it is `'meas'`.
841 Name of flux column from which to compute; same as would be passed to
842 `lsst.pipe.tasks.functors.Mag`.
845 Filters from which to compute magnitude difference.
846 Color computed is `Mag(filt2) - Mag(filt1)`.
848 _defaultDataset =
'forced_src'
849 _dfLevels = (
'band',
'column')
855 raise RuntimeError(
"Cannot compute Color for %s: %s - %s " % (col, filt2, filt1))
859 self.
mag2mag2 =
Mag(col, filt=filt2, **kwargs)
860 self.
mag1mag1 =
Mag(col, filt=filt1, **kwargs)
873 mag2 = self.mag2._func(df[self.filt2])
874 mag1 = self.mag1._func(df[self.filt1])
879 return [self.
mag1mag1.col, self.
mag2mag2.col]
886 return f
'{self.filt2} - {self.filt1} ({self.col})'
890 return f
"{self.col}_{self.filt2.replace('-', '')}m{self.filt1.replace('-', '')}"
894 """Main function of this subclass is to override the dropna=True
897 _allow_difference =
False
902 return super().
__call__(parq, dropna=
False, **kwargs)
906 _columns = [
"base_ClassificationExtendedness_value"]
907 _column =
"base_ClassificationExtendedness_value"
912 test = (x < 0.5).astype(int)
913 test = test.mask(mask, 2)
917 categories = [
'galaxy',
'star', self.
_null_label_null_label]
918 label = pd.Series(pd.Categorical.from_codes(test, categories=categories),
919 index=x.index, name=
'label')
921 label = label.astype(str)
926 _columns = [
'numStarFlags']
927 labels = {
"star": 0,
"maybe": 1,
"notStar": 2}
933 n = len(x.unique()) - 1
935 labels = [
'noStar',
'maybe',
'star']
936 label = pd.Series(pd.cut(x, [-1, 0, n-1, n], labels=labels),
937 index=x.index, name=
'label')
940 label = label.astype(str)
946 name =
'Deconvolved Moments'
947 shortname =
'deconvolvedMoments'
948 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
949 "ext_shapeHSM_HsmSourceMoments_yy",
950 "base_SdssShape_xx",
"base_SdssShape_yy",
951 "ext_shapeHSM_HsmPsfMoments_xx",
952 "ext_shapeHSM_HsmPsfMoments_yy")
955 """Calculate deconvolved moments"""
956 if "ext_shapeHSM_HsmSourceMoments_xx" in df.columns:
957 hsm = df[
"ext_shapeHSM_HsmSourceMoments_xx"] + df[
"ext_shapeHSM_HsmSourceMoments_yy"]
959 hsm = np.ones(len(df))*np.nan
960 sdss = df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]
961 if "ext_shapeHSM_HsmPsfMoments_xx" in df.columns:
962 psf = df[
"ext_shapeHSM_HsmPsfMoments_xx"] + df[
"ext_shapeHSM_HsmPsfMoments_yy"]
967 raise RuntimeError(
'No psf shape parameter found in catalog')
969 return hsm.where(np.isfinite(hsm), sdss) - psf
973 """Functor to calculate SDSS trace radius size for sources"""
974 name =
"SDSS Trace Size"
975 shortname =
'sdssTrace'
976 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy")
979 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
984 """Functor to calculate SDSS trace radius size difference (%) between object and psf model"""
985 name =
"PSF - SDSS Trace Size"
986 shortname =
'psf_sdssTrace'
987 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy",
988 "base_SdssShape_psf_xx",
"base_SdssShape_psf_yy")
991 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
992 psfSize = np.sqrt(0.5*(df[
"base_SdssShape_psf_xx"] + df[
"base_SdssShape_psf_yy"]))
993 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
998 """Functor to calculate HSM trace radius size for sources"""
999 name =
'HSM Trace Size'
1000 shortname =
'hsmTrace'
1001 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1002 "ext_shapeHSM_HsmSourceMoments_yy")
1004 def _func(self, df):
1005 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1006 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1011 """Functor to calculate HSM trace radius size difference (%) between object and psf model"""
1012 name =
'PSF - HSM Trace Size'
1013 shortname =
'psf_HsmTrace'
1014 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1015 "ext_shapeHSM_HsmSourceMoments_yy",
1016 "ext_shapeHSM_HsmPsfMoments_xx",
1017 "ext_shapeHSM_HsmPsfMoments_yy")
1019 def _func(self, df):
1020 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1021 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1022 psfSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmPsfMoments_xx"]
1023 + df[
"ext_shapeHSM_HsmPsfMoments_yy"]))
1024 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1029 name =
'HSM Psf FWHM'
1030 _columns = (
'ext_shapeHSM_HsmPsfMoments_xx',
'ext_shapeHSM_HsmPsfMoments_yy')
1033 SIGMA2FWHM = 2*np.sqrt(2*np.log(2))
1035 def _func(self, df):
1037 0.5*(df[
'ext_shapeHSM_HsmPsfMoments_xx'] + df[
'ext_shapeHSM_HsmPsfMoments_yy']))
1041 name =
"Distortion Ellipticity (e1)"
1042 shortname =
"Distortion"
1055 def _func(self, df):
1060 name =
"Ellipticity e2"
1072 def _func(self, df):
1073 return 2*df[self.
colXYcolXY] / (df[self.
colXXcolXX] + df[self.
colYYcolYY])
1088 def _func(self, df):
1089 return (df[self.
colXXcolXX]*df[self.
colYYcolYY] - df[self.
colXYcolXY]**2)**0.25
1093 """Computations using the stored localWcs.
1095 name =
"LocalWcsOperations"
1110 """Compute the distance on the sphere from x2, y1 to x1, y1.
1118 cd11 : `pandas.Series`
1119 [1, 1] element of the local Wcs affine transform.
1120 cd11 : `pandas.Series`
1121 [1, 1] element of the local Wcs affine transform.
1122 cd12 : `pandas.Series`
1123 [1, 2] element of the local Wcs affine transform.
1124 cd21 : `pandas.Series`
1125 [2, 1] element of the local Wcs affine transform.
1126 cd22 : `pandas.Series`
1127 [2, 2] element of the local Wcs affine transform.
1132 RA and dec conversion of x and y given the local Wcs. Returned
1133 units are in radians.
1136 return (x * cd11 + y * cd12, x * cd21 + y * cd22)
1139 """Compute the local pixel scale conversion.
1143 ra1 : `pandas.Series`
1144 Ra of the first coordinate in radians.
1145 dec1 : `pandas.Series`
1146 Dec of the first coordinate in radians.
1147 ra2 : `pandas.Series`
1148 Ra of the second coordinate in radians.
1149 dec2 : `pandas.Series`
1150 Dec of the second coordinate in radians.
1154 dist : `pandas.Series`
1155 Distance on the sphere in radians.
1157 deltaDec = dec2 - dec1
1159 return 2 * np.arcsin(
1161 np.sin(deltaDec / 2) ** 2
1162 + np.cos(dec2) * np.cos(dec1) * np.sin(deltaRa / 2) ** 2))
1165 """Compute the distance on the sphere from x2, y1 to x1, y1.
1169 x1 : `pandas.Series`
1171 y1 : `pandas.Series`
1173 x2 : `pandas.Series`
1175 y2 : `pandas.Series`
1177 cd11 : `pandas.Series`
1178 [1, 1] element of the local Wcs affine transform.
1179 cd11 : `pandas.Series`
1180 [1, 1] element of the local Wcs affine transform.
1181 cd12 : `pandas.Series`
1182 [1, 2] element of the local Wcs affine transform.
1183 cd21 : `pandas.Series`
1184 [2, 1] element of the local Wcs affine transform.
1185 cd22 : `pandas.Series`
1186 [2, 2] element of the local Wcs affine transform.
1190 Distance : `pandas.Series`
1191 Arcseconds per pixel at the location of the local WC
1193 ra1, dec1 = self.
computeDeltaRaDeccomputeDeltaRaDec(x1, y1, cd11, cd12, cd21, cd22)
1194 ra2, dec2 = self.
computeDeltaRaDeccomputeDeltaRaDec(x2, y2, cd11, cd12, cd21, cd22)
1200 """Compute the local pixel scale from the stored CDMatrix.
1212 """Compute the local pixel to scale conversion in arcseconds.
1216 cd11 : `pandas.Series`
1217 [1, 1] element of the local Wcs affine transform in radians.
1218 cd11 : `pandas.Series`
1219 [1, 1] element of the local Wcs affine transform in radians.
1220 cd12 : `pandas.Series`
1221 [1, 2] element of the local Wcs affine transform in radians.
1222 cd21 : `pandas.Series`
1223 [2, 1] element of the local Wcs affine transform in radians.
1224 cd22 : `pandas.Series`
1225 [2, 2] element of the local Wcs affine transform in radians.
1229 pixScale : `pandas.Series`
1230 Arcseconds per pixel at the location of the local WC
1232 return 3600 * np.degrees(np.sqrt(np.fabs(cd11 * cd22 - cd12 * cd21)))
1234 def _func(self, df):
1242 """Convert a value in units pixels squared to units arcseconds squared.
1261 return f
"{self.col}_asArcseconds"
1265 return [self.
colcol,
1271 def _func(self, df):
1279 """Convert a value in units pixels to units arcseconds.
1298 return f
"{self.col}_asArcsecondsSq"
1302 return [self.
colcol,
1308 def _func(self, df):
1313 return df[self.
colcol] * pixScale * pixScale
1317 name =
'Reference Band'
1318 shortname =
'refBand'
1322 return [
"merge_measurement_i",
1323 "merge_measurement_r",
1324 "merge_measurement_z",
1325 "merge_measurement_y",
1326 "merge_measurement_g"]
1328 def _func(self, df):
1329 def getFilterAliasName(row):
1331 colName = row.idxmax()
1332 return colName.replace(
'merge_measurement_',
'')
1334 return df[self.
columnscolumnscolumns].apply(getFilterAliasName, axis=1)
1339 AB_FLUX_SCALE = (0 * u.ABmag).to_value(u.nJy)
1340 LOG_AB_FLUX_SCALE = 12.56
1341 FIVE_OVER_2LOG10 = 1.085736204758129569
1345 def __init__(self, colFlux, colFluxErr=None, calib=None, **kwargs):
1351 if calib
is not None:
1361 return [self.
colcol]
1365 return f
'mag_{self.col}'
1369 if np.abs(a) < np.abs(b):
1374 return np.abs(a) * np.sqrt(1. + q*q)
1380 with np.warnings.catch_warnings():
1381 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1382 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
1383 return -2.5 * np.log10(dn/fluxMag0)
1386 retVal = self.
vhypotvhypot(dn * fluxMag0Err, dnErr * fluxMag0)
1387 retVal *= self.
AB_FLUX_SCALEAB_FLUX_SCALE / fluxMag0 / fluxMag0
1391 retVal = self.
dn2fluxErrdn2fluxErr(dn, dnErr, fluxMag0, fluxMag0Err) / self.
dn2fluxdn2flux(dn, fluxMag0)
1396 def _func(self, df):
1405 def _func(self, df):
1407 return pd.Series(retArr, index=df.index)
1411 def _func(self, df):
1420 def _func(self, df):
1422 return pd.Series(retArr, index=df.index)
1426 """Base class for calibrating the specified instrument flux column using
1427 the local photometric calibration.
1432 Name of the instrument flux column.
1433 instFluxErrCol : `str`
1434 Name of the assocated error columns for ``instFluxCol``.
1435 photoCalibCol : `str`
1436 Name of local calibration column.
1437 photoCalibErrCol : `str`
1438 Error associated with ``photoCalibCol``
1448 logNJanskyToAB = (1 * u.nJy).to_value(u.ABmag)
1463 """Convert instrument flux to nanojanskys.
1467 instFlux : `numpy.ndarray` or `pandas.Series`
1468 Array of instrument flux measurements
1469 localCalib : `numpy.ndarray` or `pandas.Series`
1470 Array of local photometric calibration estimates.
1474 calibFlux : `numpy.ndarray` or `pandas.Series`
1475 Array of calibrated flux measurements.
1477 return instFlux * localCalib
1480 """Convert instrument flux to nanojanskys.
1484 instFlux : `numpy.ndarray` or `pandas.Series`
1485 Array of instrument flux measurements
1486 instFluxErr : `numpy.ndarray` or `pandas.Series`
1487 Errors on associated ``instFlux`` values
1488 localCalib : `numpy.ndarray` or `pandas.Series`
1489 Array of local photometric calibration estimates.
1490 localCalibErr : `numpy.ndarray` or `pandas.Series`
1491 Errors on associated ``localCalib`` values
1495 calibFluxErr : `numpy.ndarray` or `pandas.Series`
1496 Errors on calibrated flux measurements.
1498 return np.hypot(instFluxErr * localCalib, instFlux * localCalibErr)
1501 """Convert instrument flux to nanojanskys.
1505 instFlux : `numpy.ndarray` or `pandas.Series`
1506 Array of instrument flux measurements
1507 localCalib : `numpy.ndarray` or `pandas.Series`
1508 Array of local photometric calibration estimates.
1512 calibMag : `numpy.ndarray` or `pandas.Series`
1513 Array of calibrated AB magnitudes.
1518 """Convert instrument flux err to nanojanskys.
1522 instFlux : `numpy.ndarray` or `pandas.Series`
1523 Array of instrument flux measurements
1524 instFluxErr : `numpy.ndarray` or `pandas.Series`
1525 Errors on associated ``instFlux`` values
1526 localCalib : `numpy.ndarray` or `pandas.Series`
1527 Array of local photometric calibration estimates.
1528 localCalibErr : `numpy.ndarray` or `pandas.Series`
1529 Errors on associated ``localCalib`` values
1533 calibMagErr: `numpy.ndarray` or `pandas.Series`
1534 Error on calibrated AB magnitudes.
1537 return 2.5 / np.log(10) * err / self.
instFluxToNanojanskyinstFluxToNanojansky(instFlux, instFluxErr)
1541 """Compute calibrated fluxes using the local calibration value.
1557 return f
'flux_{self.instFluxCol}'
1559 def _func(self, df):
1564 """Compute calibrated flux errors using the local calibration value.
1581 return f
'fluxErr_{self.instFluxCol}'
1583 def _func(self, df):
1589 """Compute calibrated AB magnitudes using the local calibration value.
1605 return f
'mag_{self.instFluxCol}'
1607 def _func(self, df):
1613 """Compute calibrated AB magnitude errors using the local calibration value.
1630 return f
'magErr_{self.instFluxCol}'
1632 def _func(self, df):
1640 """Compute absolute mean of dipole fluxes.
1649 LocalDipoleMeanFluxErr
1651 LocalDipoleDiffFluxErr
1681 return f
'dipMeanFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1683 def _func(self, df):
1689 """Compute the error on the absolute mean of dipole fluxes.
1698 LocalDipoleMeanFluxErr
1700 LocalDipoleDiffFluxErr
1714 return f
'dipMeanFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1716 def _func(self, df):
1725 """Compute the absolute difference of dipole fluxes.
1727 Value is (abs(pos) - abs(neg))
1736 LocalDipoleMeanFluxErr
1738 LocalDipoleDiffFluxErr
1749 return f
'dipDiffFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1751 def _func(self, df):
1757 """Compute the error on the absolute difference of dipole fluxes.
1766 LocalDipoleMeanFluxErr
1768 LocalDipoleDiffFluxErr
1782 return f
'dipDiffFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1784 def _func(self, df):
1793 """Base class for returning the ratio of 2 columns.
1795 Can be used to compute a Signal to Noise ratio for any input flux.
1800 Name of the column to use at the numerator in the ratio
1802 Name of the column to use as the denominator in the ratio.
1818 return f
'ratio_{self.numerator}_{self.denominator}'
1820 def _func(self, df):
1821 with np.warnings.catch_warnings():
1822 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1823 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
std::vector< SchemaItem< Flag > > * items
def multilevelColumns(self, parq, **kwargs)
def __init__(self, col, filt2, filt1, **kwargs)
def __init__(self, col, **kwargs)
def __init__(self, funcs, **kwargs)
def __call__(self, data, **kwargs)
def from_file(cls, filename, **kwargs)
def from_yaml(cls, translationDefinition, **kwargs)
def renameCol(cls, col, renameRules)
def multilevelColumns(self, data, **kwargs)
def pixelScaleArcseconds(self, cd11, cd12, cd21, cd22)
def __init__(self, col, colCD_1_1, colCD_1_2, colCD_2_1, colCD_2_2, **kwargs)
def __init__(self, col, colCD_1_1, colCD_1_2, colCD_2_1, colCD_2_2, **kwargs)
def __init__(self, col, **kwargs)
def __init__(self, expr, **kwargs)
def __init__(self, **kwargs)
def __call__(self, catalog, **kwargs)
def __init__(self, colXX, colXY, colYY, **kwargs)
def __init__(self, colXX, colXY, colYY, **kwargs)
def __call__(self, data, dropna=False)
def _get_data(self, data)
def _func(self, df, dropna=True)
def multilevelColumns(self, data, columnIndex=None, returnTuple=False)
def _get_data_columnLevelNames(self, data, columnIndex=None)
def difference(self, data1, data2, **kwargs)
def __init__(self, filt=None, dataset=None, noDup=None)
def _get_columnIndex(self, data)
def _colsFromDict(self, colDict, columnIndex=None)
def _get_data_columnLevels(self, data, columnIndex=None)
def __call__(self, parq, dropna=False, **kwargs)
def __init__(self, instFluxPosCol, instFluxNegCol, instFluxPosErrCol, instFluxNegErrCol, photoCalibCol, photoCalibErrCol, **kwargs)
def instFluxToNanojansky(self, instFlux, localCalib)
def instFluxErrToMagnitudeErr(self, instFlux, instFluxErr, localCalib, localCalibErr)
def __init__(self, instFluxCol, instFluxErrCol, photoCalibCol, photoCalibErrCol, **kwargs)
def instFluxErrToNanojanskyErr(self, instFlux, instFluxErr, localCalib, localCalibErr)
def instFluxToMagnitude(self, instFlux, localCalib)
def __init__(self, colCD_1_1, colCD_1_2, colCD_2_1, colCD_2_2, **kwargs)
def computeDeltaRaDec(self, x, y, cd11, cd12, cd21, cd22)
def computeSkySeperation(self, ra1, dec1, ra2, dec2)
def getSkySeperationFromPixel(self, x1, y1, x2, y2, cd11, cd12, cd21, cd22)
def __init__(self, col1, col2, **kwargs)
def __init__(self, *args, **kwargs)
def __init__(self, col, calib=None, **kwargs)
def dn2mag(self, dn, fluxMag0)
def dn2flux(self, dn, fluxMag0)
def dn2fluxErr(self, dn, dnErr, fluxMag0, fluxMag0Err)
def dn2MagErr(self, dn, dnErr, fluxMag0, fluxMag0Err)
def __init__(self, colFlux, colFluxErr=None, calib=None, **kwargs)
def __call__(self, catalog, **kwargs)
def __init__(self, **kwargs)
def __init__(self, colXX, colXY, colYY, **kwargs)
def __init__(self, numerator, denominator, **kwargs)
daf::base::PropertyList * list
daf::base::PropertySet * set
def mag_aware_eval(df, expr)
def init_fromDict(initDict, basePath='lsst.pipe.tasks.functors', typeKey='functor', name=None)