24 from itertools
import product
29 import astropy.units
as u
32 from lsst.daf.butler
import DeferredDatasetHandle
36 from .parquetTable
import ParquetTable, MultilevelParquetTable
40 typeKey='functor', name=None):
41 """Initialize an object defined in a dictionary
43 The object needs to be importable as
44 f'{basePath}.{initDict[typeKey]}'
45 The positional and keyword arguments (if any) are contained in
46 "args" and "kwargs" entries in the dictionary, respectively.
47 This is used in `functors.CompositeFunctor.from_yaml` to initialize
48 a composite functor from a specification in a YAML file.
53 Dictionary describing object's initialization. Must contain
54 an entry keyed by ``typeKey`` that is the name of the object,
55 relative to ``basePath``.
57 Path relative to module in which ``initDict[typeKey]`` is defined.
59 Key of ``initDict`` that is the name of the object
60 (relative to `basePath`).
62 initDict = initDict.copy()
64 pythonType =
doImport(f
'{basePath}.{initDict.pop(typeKey)}')
66 if 'args' in initDict:
67 args = initDict.pop(
'args')
68 if isinstance(args, str):
71 element = pythonType(*args, **initDict)
72 except Exception
as e:
73 message = f
'Error in constructing functor "{name}" of type {pythonType.__name__} with args: {args}'
74 raise type(e)(message, e.args)
79 """Define and execute a calculation on a ParquetTable
81 The `__call__` method accepts either a `ParquetTable` object or a
82 `DeferredDatasetHandle`, and returns the
83 result of the calculation as a single column. Each functor defines what
84 columns are needed for the calculation, and only these columns are read
85 from the `ParquetTable`.
87 The action of `__call__` consists of two steps: first, loading the
88 necessary columns from disk into memory as a `pandas.DataFrame` object;
89 and second, performing the computation on this dataframe and returning the
93 To define a new `Functor`, a subclass must define a `_func` method,
94 that takes a `pandas.DataFrame` and returns result in a `pandas.Series`.
95 In addition, it must define the following attributes
97 * `_columns`: The columns necessary to perform the calculation
98 * `name`: A name appropriate for a figure axis label
99 * `shortname`: A name appropriate for use as a dictionary key
101 On initialization, a `Functor` should declare what band (`filt` kwarg)
102 and dataset (e.g. `'ref'`, `'meas'`, `'forced_src'`) it is intended to be
103 applied to. This enables the `_get_data` method to extract the proper
104 columns from the parquet file. If not specified, the dataset will fall back
105 on the `_defaultDataset`attribute. If band is not specified and `dataset`
106 is anything other than `'ref'`, then an error will be raised when trying to
107 perform the calculation.
109 Originally, `Functor` was set up to expect
110 datasets formatted like the `deepCoadd_obj` dataset; that is, a
111 dataframe with a multi-level column index, with the levels of the
112 column index being `band`, `dataset`, and `column`.
113 It has since been generalized to apply to dataframes without mutli-level
114 indices and multi-level indices with just `dataset` and `column` levels.
115 In addition, the `_get_data` method that reads
116 the dataframe from the `ParquetTable` will return a dataframe with column
117 index levels defined by the `_dfLevels` attribute; by default, this is
120 The `_dfLevels` attributes should generally not need to
121 be changed, unless `_func` needs columns from multiple filters or datasets
122 to do the calculation.
123 An example of this is the `lsst.pipe.tasks.functors.Color` functor, for
124 which `_dfLevels = ('band', 'column')`, and `_func` expects the dataframe
125 it gets to have those levels in the column index.
130 Filter upon which to do the calculation
133 Dataset upon which to do the calculation
134 (e.g., 'ref', 'meas', 'forced_src').
138 _defaultDataset =
'ref'
139 _dfLevels = (
'column',)
140 _defaultNoDup =
False
142 def __init__(self, filt=None, dataset=None, noDup=None):
149 if self.
_noDup_noDup
is not None:
156 """Columns required to perform calculation
158 if not hasattr(self,
'_columns'):
159 raise NotImplementedError(
'Must define columns property or _columns attribute')
162 def _get_data_columnLevels(self, data, columnIndex=None):
163 """Gets the names of the column index levels
165 This should only be called in the context of a multilevel table.
166 The logic here is to enable this to work both with the gen2 `MultilevelParquetTable`
167 and with the gen3 `DeferredDatasetHandle`.
171 data : `MultilevelParquetTable` or `DeferredDatasetHandle`
173 columnnIndex (optional): pandas `Index` object
174 if not passed, then it is read from the `DeferredDatasetHandle`
176 if isinstance(data, DeferredDatasetHandle):
177 if columnIndex
is None:
178 columnIndex = data.get(component=
"columns")
179 if columnIndex
is not None:
180 return columnIndex.names
181 if isinstance(data, MultilevelParquetTable):
182 return data.columnLevels
184 raise TypeError(f
"Unknown type for data: {type(data)}!")
186 def _get_data_columnLevelNames(self, data, columnIndex=None):
187 """Gets the content of each of the column levels for a multilevel table
189 Similar to `_get_data_columnLevels`, this enables backward compatibility with gen2.
191 Mirrors original gen2 implementation within `pipe.tasks.parquetTable.MultilevelParquetTable`
193 if isinstance(data, DeferredDatasetHandle):
194 if columnIndex
is None:
195 columnIndex = data.get(component=
"columns")
196 if columnIndex
is not None:
197 columnLevels = columnIndex.names
199 level:
list(np.unique(np.array([c
for c
in columnIndex])[:, i]))
200 for i, level
in enumerate(columnLevels)
202 return columnLevelNames
203 if isinstance(data, MultilevelParquetTable):
204 return data.columnLevelNames
206 raise TypeError(f
"Unknown type for data: {type(data)}!")
208 def _colsFromDict(self, colDict, columnIndex=None):
209 """Converts dictionary column specficiation to a list of columns
211 This mirrors the original gen2 implementation within `pipe.tasks.parquetTable.MultilevelParquetTable`
216 for i, lev
in enumerate(columnLevels):
218 if isinstance(colDict[lev], str):
219 new_colDict[lev] = [colDict[lev]]
221 new_colDict[lev] = colDict[lev]
223 new_colDict[lev] = columnIndex.levels[i]
225 levelCols = [new_colDict[lev]
for lev
in columnLevels]
226 cols = product(*levelCols)
230 """Returns columns needed by functor from multilevel dataset
232 To access tables with multilevel column structure, the `MultilevelParquetTable`
233 or `DeferredDatasetHandle` need to be passed either a list of tuples or a
238 data : `MultilevelParquetTable` or `DeferredDatasetHandle`
240 columnIndex (optional): pandas `Index` object
241 either passed or read in from `DeferredDatasetHandle`.
244 If true, then return a list of tuples rather than the column dictionary
245 specification. This is set to `True` by `CompositeFunctor` in order to be able to
246 combine columns from the various component functors.
249 if isinstance(data, DeferredDatasetHandle)
and columnIndex
is None:
250 columnIndex = data.get(component=
"columns")
255 columnDict = {
'column': self.
columnscolumns,
256 'dataset': self.
datasetdataset}
257 if self.
filtfilt
is None:
259 if "band" in columnLevels:
260 if self.
datasetdataset ==
"ref":
261 columnDict[
"band"] = columnLevelNames[
"band"][0]
263 raise ValueError(f
"'filt' not set for functor {self.name}"
264 f
"(dataset {self.dataset}) "
266 "contains multiple filters in column index. "
267 "Set 'filt' or set 'dataset' to 'ref'.")
269 columnDict[
'band'] = self.
filtfilt
271 if isinstance(data, MultilevelParquetTable):
272 return data._colsFromDict(columnDict)
273 elif isinstance(data, DeferredDatasetHandle):
275 return self.
_colsFromDict_colsFromDict(columnDict, columnIndex=columnIndex)
279 def _func(self, df, dropna=True):
280 raise NotImplementedError(
'Must define calculation on dataframe')
282 def _get_columnIndex(self, data):
283 """Return columnIndex
286 if isinstance(data, DeferredDatasetHandle):
287 return data.get(component=
"columns")
291 def _get_data(self, data):
292 """Retrieve dataframe necessary for calculation.
294 The data argument can be a DataFrame, a ParquetTable instance, or a gen3 DeferredDatasetHandle
296 Returns dataframe upon which `self._func` can act.
298 N.B. while passing a raw pandas `DataFrame` *should* work here, it has not been tested.
300 if isinstance(data, pd.DataFrame):
305 is_multiLevel = isinstance(data, MultilevelParquetTable)
or isinstance(columnIndex, pd.MultiIndex)
308 if isinstance(data, ParquetTable)
and not is_multiLevel:
310 df = data.toDataFrame(columns=columns)
319 if isinstance(data, MultilevelParquetTable):
321 df = data.toDataFrame(columns=columns, droplevels=
False)
322 elif isinstance(data, DeferredDatasetHandle):
324 df = data.get(parameters={
"columns": columns})
332 def _setLevels(self, df):
333 levelsToDrop = [n
for n
in df.columns.names
if n
not in self.
_dfLevels_dfLevels]
334 df.columns = df.columns.droplevel(levelsToDrop)
337 def _dropna(self, vals):
343 vals = self.
_func_func(df)
345 vals = self.
failfail(df)
347 vals = self.
_dropna_dropna(vals)
352 """Computes difference between functor called on two different ParquetTable objects
354 return self(data1, **kwargs) - self(data2, **kwargs)
357 return pd.Series(np.full(len(df), np.nan), index=df.index)
361 """Full name of functor (suitable for figure labels)
363 return NotImplementedError
367 """Short name of functor (suitable for column name/dict key)
373 """Perform multiple calculations at once on a catalog
375 The role of a `CompositeFunctor` is to group together computations from
376 multiple functors. Instead of returning `pandas.Series` a
377 `CompositeFunctor` returns a `pandas.Dataframe`, with the column names
378 being the keys of `funcDict`.
380 The `columns` attribute of a `CompositeFunctor` is the union of all columns
381 in all the component functors.
383 A `CompositeFunctor` does not use a `_func` method itself; rather,
384 when a `CompositeFunctor` is called, all its columns are loaded
385 at once, and the resulting dataframe is passed to the `_func` method of each component
386 functor. This has the advantage of only doing I/O (reading from parquet file) once,
387 and works because each individual `_func` method of each component functor does not
388 care if there are *extra* columns in the dataframe being passed; only that it must contain
389 *at least* the `columns` it expects.
391 An important and useful class method is `from_yaml`, which takes as argument the path to a YAML
392 file specifying a collection of functors.
396 funcs : `dict` or `list`
397 Dictionary or list of functors. If a list, then it will be converted
398 into a dictonary according to the `.shortname` attribute of each functor.
405 if type(funcs) == dict:
408 self.
funcDictfuncDict = {f.shortname: f
for f
in funcs}
410 self.
_filt_filt =
None
416 return self.
_filt_filt
423 self.
_filt_filt = filt
426 if isinstance(new, dict):
428 elif isinstance(new, CompositeFunctor):
431 raise TypeError(
'Can only update with dictionary or CompositeFunctor.')
439 return list(
set([x
for y
in [f.columns
for f
in self.
funcDictfuncDict.values()]
for x
in y]))
448 f.multilevelColumns(data, returnTuple=
True, **kwargs)
for f
in self.
funcDictfuncDict.values()
456 """Apply the functor to the data table
460 data : `lsst.daf.butler.DeferredDatasetHandle`,
461 `lsst.pipe.tasks.parquetTable.MultilevelParquetTable`,
462 `lsst.pipe.tasks.parquetTable.ParquetTable`,
463 or `pandas.DataFrame`.
464 The table or a pointer to a table on disk from which columns can
470 is_multiLevel = isinstance(data, MultilevelParquetTable)
or isinstance(columnIndex, pd.MultiIndex)
476 if isinstance(data, MultilevelParquetTable):
478 df = data.toDataFrame(columns=columns, droplevels=
False)
479 elif isinstance(data, DeferredDatasetHandle):
481 df = data.get(parameters={
"columns": columns})
486 subdf = f._setLevels(
487 df[f.multilevelColumns(data, returnTuple=
True, columnIndex=columnIndex)]
489 valDict[k] = f._func(subdf)
490 except Exception
as e:
492 valDict[k] = f.fail(subdf)
497 if isinstance(data, DeferredDatasetHandle):
500 elif isinstance(data, pd.DataFrame):
507 valDict = {k: f._func(df)
for k, f
in self.
funcDictfuncDict.
items()}
510 valDf = pd.concat(valDict, axis=1)
512 print([(k,
type(v))
for k, v
in valDict.items()])
515 if kwargs.get(
'dropna',
False):
516 valDf = valDf.dropna(how=
'any')
522 if renameRules
is None:
524 for old, new
in renameRules:
525 if col.startswith(old):
526 col = col.replace(old, new)
532 filename = os.path.expandvars(filename)
533 with open(filename)
as f:
534 translationDefinition = yaml.safe_load(f)
536 return cls.
from_yamlfrom_yaml(translationDefinition, **kwargs)
541 for func, val
in translationDefinition[
'funcs'].
items():
544 if 'flag_rename_rules' in translationDefinition:
545 renameRules = translationDefinition[
'flag_rename_rules']
549 if 'calexpFlags' in translationDefinition:
550 for flag
in translationDefinition[
'calexpFlags']:
551 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'calexp')
553 if 'refFlags' in translationDefinition:
554 for flag
in translationDefinition[
'refFlags']:
555 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'ref')
557 if 'forcedFlags' in translationDefinition:
558 for flag
in translationDefinition[
'forcedFlags']:
559 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'forced_src')
561 if 'flags' in translationDefinition:
562 for flag
in translationDefinition[
'flags']:
563 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'meas')
565 return cls(funcs, **kwargs)
569 """Evaluate an expression on a DataFrame, knowing what the 'mag' function means
571 Builds on `pandas.DataFrame.eval`, which parses and executes math on dataframes.
575 df : pandas.DataFrame
576 Dataframe on which to evaluate expression.
582 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>)/log(10)', expr)
583 val = df.eval(expr_new, truediv=
True)
585 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>_instFlux)/log(10)', expr)
586 val = df.eval(expr_new, truediv=
True)
591 """Arbitrary computation on a catalog
593 Column names (and thus the columns to be loaded from catalog) are found
594 by finding all words and trying to ignore all "math-y" words.
599 Expression to evaluate, to be parsed and executed by `mag_aware_eval`.
601 _ignore_words = (
'mag',
'sin',
'cos',
'exp',
'log',
'sqrt')
613 flux_cols = re.findall(
r'mag\(\s*(\w+)\s*\)', self.
exprexpr)
615 cols = [c
for c
in re.findall(
r'[a-zA-Z_]+', self.
exprexpr)
if c
not in self.
_ignore_words_ignore_words]
618 if not re.search(
'_instFlux$', c):
619 cols.append(f
'{c}_instFlux')
624 return list(
set([c
for c
in cols
if c
not in not_a_col]))
631 """Get column with specified name
647 return df[self.
colcol]
651 """Return the value of the index for each object
654 columns = [
'coord_ra']
655 _defaultDataset =
'ref'
659 return pd.Series(df.index, index=df.index)
664 _allow_difference =
False
668 return pd.Series(df.index, index=df.index)
672 col =
'base_Footprint_nPix'
676 """Base class for coordinate column, in degrees
685 output = df[self.
colcol] * 180 / np.pi
if self.
_radians_radians
else df[self.
colcol]
690 """Right Ascension, in degrees
696 super().
__init__(
'coord_ra', **kwargs)
699 return super().
__call__(catalog, **kwargs)
703 """Declination, in degrees
709 super().
__init__(
'coord_dec', **kwargs)
712 return super().
__call__(catalog, **kwargs)
716 """Compute the level 20 HtmIndex for the catalog.
731 def computePixel(row):
740 return self.
pixelatorpixelator.index(sphPoint.getVector())
742 return df.apply(computePixel, axis=1)
746 if not col.endswith(
'_instFlux'):
752 if not col.endswith(
'_instFluxErr'):
753 col +=
'_instFluxErr'
758 """Compute calibrated magnitude
760 Takes a `calib` argument, which returns the flux at mag=0
761 as `calib.getFluxMag0()`. If not provided, then the default
762 `fluxMag0` is 63095734448.0194, which is default for HSC.
763 This default should be removed in DM-21955
765 This calculation hides warnings about invalid values and dividing by zero.
767 As for all functors, a `dataset` and `filt` kwarg should be provided upon
768 initialization. Unlike the default `Functor`, however, the default dataset
769 for a `Mag` is `'meas'`, rather than `'ref'`.
774 Name of flux column from which to compute magnitude. Can be parseable
775 by `lsst.pipe.tasks.functors.fluxName` function---that is, you can pass
776 `'modelfit_CModel'` instead of `'modelfit_CModel_instFlux'`) and it will
778 calib : `lsst.afw.image.calib.Calib` (optional)
779 Object that knows zero point.
781 _defaultDataset =
'meas'
786 if calib
is not None:
790 self.
fluxMag0fluxMag0 = 63095734448.0194
799 with np.warnings.catch_warnings():
800 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
801 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
802 return -2.5*np.log10(df[self.
colcol] / self.
fluxMag0fluxMag0)
806 return f
'mag_{self.col}'
810 """Compute calibrated magnitude uncertainty
812 Takes the same `calib` object as `lsst.pipe.tasks.functors.Mag`.
817 calib : `lsst.afw.image.calib.Calib` (optional)
818 Object that knows zero point.
823 if self.
calibcalib
is not None:
830 return [self.
colcol, self.
colcol +
'Err']
833 with np.warnings.catch_warnings():
834 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
835 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
837 x = df[fluxErrCol] / df[fluxCol]
839 magErr = (2.5 / np.log(10.)) * np.sqrt(x*x + y*y)
844 return super().name +
'_err'
852 return (df[self.
colcol] / self.
fluxMag0fluxMag0) * 1e9
856 _defaultDataset =
'meas'
858 """Functor to calculate magnitude difference"""
867 return [self.
col1col1, self.
col2col2]
870 with np.warnings.catch_warnings():
871 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
872 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
873 return -2.5*np.log10(df[self.
col1col1]/df[self.
col2col2])
877 return f
'(mag_{self.col1} - mag_{self.col2})'
881 return f
'magDiff_{self.col1}_{self.col2}'
885 """Compute the color between two filters
887 Computes color by initializing two different `Mag`
888 functors based on the `col` and filters provided, and
889 then returning the difference.
891 This is enabled by the `_func` expecting a dataframe with a
892 multilevel column index, with both `'band'` and `'column'`,
893 instead of just `'column'`, which is the `Functor` default.
894 This is controlled by the `_dfLevels` attribute.
896 Also of note, the default dataset for `Color` is `forced_src'`,
897 whereas for `Mag` it is `'meas'`.
902 Name of flux column from which to compute; same as would be passed to
903 `lsst.pipe.tasks.functors.Mag`.
906 Filters from which to compute magnitude difference.
907 Color computed is `Mag(filt2) - Mag(filt1)`.
909 _defaultDataset =
'forced_src'
910 _dfLevels = (
'band',
'column')
916 raise RuntimeError(
"Cannot compute Color for %s: %s - %s " % (col, filt2, filt1))
920 self.
mag2mag2 =
Mag(col, filt=filt2, **kwargs)
921 self.
mag1mag1 =
Mag(col, filt=filt1, **kwargs)
934 mag2 = self.mag2._func(df[self.filt2])
935 mag1 = self.mag1._func(df[self.filt1])
940 return [self.
mag1mag1.col, self.
mag2mag2.col]
947 return f
'{self.filt2} - {self.filt1} ({self.col})'
951 return f
"{self.col}_{self.filt2.replace('-', '')}m{self.filt1.replace('-', '')}"
955 """Main function of this subclass is to override the dropna=True
958 _allow_difference =
False
963 return super().
__call__(parq, dropna=
False, **kwargs)
967 _columns = [
"base_ClassificationExtendedness_value"]
968 _column =
"base_ClassificationExtendedness_value"
973 test = (x < 0.5).astype(int)
974 test = test.mask(mask, 2)
978 categories = [
'galaxy',
'star', self.
_null_label_null_label]
979 label = pd.Series(pd.Categorical.from_codes(test, categories=categories),
980 index=x.index, name=
'label')
982 label = label.astype(str)
987 _columns = [
'numStarFlags']
988 labels = {
"star": 0,
"maybe": 1,
"notStar": 2}
994 n = len(x.unique()) - 1
996 labels = [
'noStar',
'maybe',
'star']
997 label = pd.Series(pd.cut(x, [-1, 0, n-1, n], labels=labels),
998 index=x.index, name=
'label')
1001 label = label.astype(str)
1007 name =
'Deconvolved Moments'
1008 shortname =
'deconvolvedMoments'
1009 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1010 "ext_shapeHSM_HsmSourceMoments_yy",
1011 "base_SdssShape_xx",
"base_SdssShape_yy",
1012 "ext_shapeHSM_HsmPsfMoments_xx",
1013 "ext_shapeHSM_HsmPsfMoments_yy")
1015 def _func(self, df):
1016 """Calculate deconvolved moments"""
1017 if "ext_shapeHSM_HsmSourceMoments_xx" in df.columns:
1018 hsm = df[
"ext_shapeHSM_HsmSourceMoments_xx"] + df[
"ext_shapeHSM_HsmSourceMoments_yy"]
1020 hsm = np.ones(len(df))*np.nan
1021 sdss = df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]
1022 if "ext_shapeHSM_HsmPsfMoments_xx" in df.columns:
1023 psf = df[
"ext_shapeHSM_HsmPsfMoments_xx"] + df[
"ext_shapeHSM_HsmPsfMoments_yy"]
1028 raise RuntimeError(
'No psf shape parameter found in catalog')
1030 return hsm.where(np.isfinite(hsm), sdss) - psf
1034 """Functor to calculate SDSS trace radius size for sources"""
1035 name =
"SDSS Trace Size"
1036 shortname =
'sdssTrace'
1037 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy")
1039 def _func(self, df):
1040 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1045 """Functor to calculate SDSS trace radius size difference (%) between object and psf model"""
1046 name =
"PSF - SDSS Trace Size"
1047 shortname =
'psf_sdssTrace'
1048 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy",
1049 "base_SdssShape_psf_xx",
"base_SdssShape_psf_yy")
1051 def _func(self, df):
1052 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1053 psfSize = np.sqrt(0.5*(df[
"base_SdssShape_psf_xx"] + df[
"base_SdssShape_psf_yy"]))
1054 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1059 """Functor to calculate HSM trace radius size for sources"""
1060 name =
'HSM Trace Size'
1061 shortname =
'hsmTrace'
1062 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1063 "ext_shapeHSM_HsmSourceMoments_yy")
1065 def _func(self, df):
1066 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1067 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1072 """Functor to calculate HSM trace radius size difference (%) between object and psf model"""
1073 name =
'PSF - HSM Trace Size'
1074 shortname =
'psf_HsmTrace'
1075 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1076 "ext_shapeHSM_HsmSourceMoments_yy",
1077 "ext_shapeHSM_HsmPsfMoments_xx",
1078 "ext_shapeHSM_HsmPsfMoments_yy")
1080 def _func(self, df):
1081 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1082 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1083 psfSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmPsfMoments_xx"]
1084 + df[
"ext_shapeHSM_HsmPsfMoments_yy"]))
1085 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1090 name =
'HSM Psf FWHM'
1091 _columns = (
'ext_shapeHSM_HsmPsfMoments_xx',
'ext_shapeHSM_HsmPsfMoments_yy')
1094 SIGMA2FWHM = 2*np.sqrt(2*np.log(2))
1096 def _func(self, df):
1098 0.5*(df[
'ext_shapeHSM_HsmPsfMoments_xx'] + df[
'ext_shapeHSM_HsmPsfMoments_yy']))
1102 name =
"Distortion Ellipticity (e1)"
1103 shortname =
"Distortion"
1116 def _func(self, df):
1121 name =
"Ellipticity e2"
1133 def _func(self, df):
1134 return 2*df[self.
colXYcolXY] / (df[self.
colXXcolXX] + df[self.
colYYcolYY])
1149 def _func(self, df):
1150 return (df[self.
colXXcolXX]*df[self.
colYYcolYY] - df[self.
colXYcolXY]**2)**0.25
1154 """Computations using the stored localWcs.
1156 name =
"LocalWcsOperations"
1171 """Compute the distance on the sphere from x2, y1 to x1, y1.
1179 cd11 : `pandas.Series`
1180 [1, 1] element of the local Wcs affine transform.
1181 cd11 : `pandas.Series`
1182 [1, 1] element of the local Wcs affine transform.
1183 cd12 : `pandas.Series`
1184 [1, 2] element of the local Wcs affine transform.
1185 cd21 : `pandas.Series`
1186 [2, 1] element of the local Wcs affine transform.
1187 cd22 : `pandas.Series`
1188 [2, 2] element of the local Wcs affine transform.
1193 RA and dec conversion of x and y given the local Wcs. Returned
1194 units are in radians.
1197 return (x * cd11 + y * cd12, x * cd21 + y * cd22)
1200 """Compute the local pixel scale conversion.
1204 ra1 : `pandas.Series`
1205 Ra of the first coordinate in radians.
1206 dec1 : `pandas.Series`
1207 Dec of the first coordinate in radians.
1208 ra2 : `pandas.Series`
1209 Ra of the second coordinate in radians.
1210 dec2 : `pandas.Series`
1211 Dec of the second coordinate in radians.
1215 dist : `pandas.Series`
1216 Distance on the sphere in radians.
1218 deltaDec = dec2 - dec1
1220 return 2 * np.arcsin(
1222 np.sin(deltaDec / 2) ** 2
1223 + np.cos(dec2) * np.cos(dec1) * np.sin(deltaRa / 2) ** 2))
1226 """Compute the distance on the sphere from x2, y1 to x1, y1.
1230 x1 : `pandas.Series`
1232 y1 : `pandas.Series`
1234 x2 : `pandas.Series`
1236 y2 : `pandas.Series`
1238 cd11 : `pandas.Series`
1239 [1, 1] element of the local Wcs affine transform.
1240 cd11 : `pandas.Series`
1241 [1, 1] element of the local Wcs affine transform.
1242 cd12 : `pandas.Series`
1243 [1, 2] element of the local Wcs affine transform.
1244 cd21 : `pandas.Series`
1245 [2, 1] element of the local Wcs affine transform.
1246 cd22 : `pandas.Series`
1247 [2, 2] element of the local Wcs affine transform.
1251 Distance : `pandas.Series`
1252 Arcseconds per pixel at the location of the local WC
1254 ra1, dec1 = self.
computeDeltaRaDeccomputeDeltaRaDec(x1, y1, cd11, cd12, cd21, cd22)
1255 ra2, dec2 = self.
computeDeltaRaDeccomputeDeltaRaDec(x2, y2, cd11, cd12, cd21, cd22)
1261 """Compute the local pixel scale from the stored CDMatrix.
1273 """Compute the local pixel to scale conversion in arcseconds.
1277 cd11 : `pandas.Series`
1278 [1, 1] element of the local Wcs affine transform in radians.
1279 cd11 : `pandas.Series`
1280 [1, 1] element of the local Wcs affine transform in radians.
1281 cd12 : `pandas.Series`
1282 [1, 2] element of the local Wcs affine transform in radians.
1283 cd21 : `pandas.Series`
1284 [2, 1] element of the local Wcs affine transform in radians.
1285 cd22 : `pandas.Series`
1286 [2, 2] element of the local Wcs affine transform in radians.
1290 pixScale : `pandas.Series`
1291 Arcseconds per pixel at the location of the local WC
1293 return 3600 * np.degrees(np.sqrt(np.fabs(cd11 * cd22 - cd12 * cd21)))
1295 def _func(self, df):
1303 """Convert a value in units pixels squared to units arcseconds squared.
1322 return f
"{self.col}_asArcseconds"
1326 return [self.
colcol,
1332 def _func(self, df):
1340 """Convert a value in units pixels to units arcseconds.
1359 return f
"{self.col}_asArcsecondsSq"
1363 return [self.
colcol,
1369 def _func(self, df):
1374 return df[self.
colcol] * pixScale * pixScale
1378 name =
'Reference Band'
1379 shortname =
'refBand'
1383 return [
"merge_measurement_i",
1384 "merge_measurement_r",
1385 "merge_measurement_z",
1386 "merge_measurement_y",
1387 "merge_measurement_g"]
1389 def _func(self, df):
1390 def getFilterAliasName(row):
1392 colName = row.idxmax()
1393 return colName.replace(
'merge_measurement_',
'')
1395 return df[self.
columnscolumnscolumns].apply(getFilterAliasName, axis=1)
1400 AB_FLUX_SCALE = (0 * u.ABmag).to_value(u.nJy)
1401 LOG_AB_FLUX_SCALE = 12.56
1402 FIVE_OVER_2LOG10 = 1.085736204758129569
1406 def __init__(self, colFlux, colFluxErr=None, calib=None, **kwargs):
1412 if calib
is not None:
1422 return [self.
colcol]
1426 return f
'mag_{self.col}'
1430 if np.abs(a) < np.abs(b):
1435 return np.abs(a) * np.sqrt(1. + q*q)
1441 with np.warnings.catch_warnings():
1442 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1443 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
1444 return -2.5 * np.log10(dn/fluxMag0)
1447 retVal = self.
vhypotvhypot(dn * fluxMag0Err, dnErr * fluxMag0)
1448 retVal *= self.
AB_FLUX_SCALEAB_FLUX_SCALE / fluxMag0 / fluxMag0
1452 retVal = self.
dn2fluxErrdn2fluxErr(dn, dnErr, fluxMag0, fluxMag0Err) / self.
dn2fluxdn2flux(dn, fluxMag0)
1457 def _func(self, df):
1466 def _func(self, df):
1468 return pd.Series(retArr, index=df.index)
1472 def _func(self, df):
1481 def _func(self, df):
1483 return pd.Series(retArr, index=df.index)
1487 """Base class for calibrating the specified instrument flux column using
1488 the local photometric calibration.
1493 Name of the instrument flux column.
1494 instFluxErrCol : `str`
1495 Name of the assocated error columns for ``instFluxCol``.
1496 photoCalibCol : `str`
1497 Name of local calibration column.
1498 photoCalibErrCol : `str`
1499 Error associated with ``photoCalibCol``
1509 logNJanskyToAB = (1 * u.nJy).to_value(u.ABmag)
1524 """Convert instrument flux to nanojanskys.
1528 instFlux : `numpy.ndarray` or `pandas.Series`
1529 Array of instrument flux measurements
1530 localCalib : `numpy.ndarray` or `pandas.Series`
1531 Array of local photometric calibration estimates.
1535 calibFlux : `numpy.ndarray` or `pandas.Series`
1536 Array of calibrated flux measurements.
1538 return instFlux * localCalib
1541 """Convert instrument flux to nanojanskys.
1545 instFlux : `numpy.ndarray` or `pandas.Series`
1546 Array of instrument flux measurements
1547 instFluxErr : `numpy.ndarray` or `pandas.Series`
1548 Errors on associated ``instFlux`` values
1549 localCalib : `numpy.ndarray` or `pandas.Series`
1550 Array of local photometric calibration estimates.
1551 localCalibErr : `numpy.ndarray` or `pandas.Series`
1552 Errors on associated ``localCalib`` values
1556 calibFluxErr : `numpy.ndarray` or `pandas.Series`
1557 Errors on calibrated flux measurements.
1559 return np.hypot(instFluxErr * localCalib, instFlux * localCalibErr)
1562 """Convert instrument flux to nanojanskys.
1566 instFlux : `numpy.ndarray` or `pandas.Series`
1567 Array of instrument flux measurements
1568 localCalib : `numpy.ndarray` or `pandas.Series`
1569 Array of local photometric calibration estimates.
1573 calibMag : `numpy.ndarray` or `pandas.Series`
1574 Array of calibrated AB magnitudes.
1579 """Convert instrument flux err to nanojanskys.
1583 instFlux : `numpy.ndarray` or `pandas.Series`
1584 Array of instrument flux measurements
1585 instFluxErr : `numpy.ndarray` or `pandas.Series`
1586 Errors on associated ``instFlux`` values
1587 localCalib : `numpy.ndarray` or `pandas.Series`
1588 Array of local photometric calibration estimates.
1589 localCalibErr : `numpy.ndarray` or `pandas.Series`
1590 Errors on associated ``localCalib`` values
1594 calibMagErr: `numpy.ndarray` or `pandas.Series`
1595 Error on calibrated AB magnitudes.
1598 return 2.5 / np.log(10) * err / self.
instFluxToNanojanskyinstFluxToNanojansky(instFlux, instFluxErr)
1602 """Compute calibrated fluxes using the local calibration value.
1618 return f
'flux_{self.instFluxCol}'
1620 def _func(self, df):
1625 """Compute calibrated flux errors using the local calibration value.
1642 return f
'fluxErr_{self.instFluxCol}'
1644 def _func(self, df):
1650 """Compute calibrated AB magnitudes using the local calibration value.
1666 return f
'mag_{self.instFluxCol}'
1668 def _func(self, df):
1674 """Compute calibrated AB magnitude errors using the local calibration value.
1691 return f
'magErr_{self.instFluxCol}'
1693 def _func(self, df):
1701 """Compute absolute mean of dipole fluxes.
1710 LocalDipoleMeanFluxErr
1712 LocalDipoleDiffFluxErr
1742 return f
'dipMeanFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1744 def _func(self, df):
1750 """Compute the error on the absolute mean of dipole fluxes.
1759 LocalDipoleMeanFluxErr
1761 LocalDipoleDiffFluxErr
1775 return f
'dipMeanFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1777 def _func(self, df):
1786 """Compute the absolute difference of dipole fluxes.
1788 Value is (abs(pos) - abs(neg))
1797 LocalDipoleMeanFluxErr
1799 LocalDipoleDiffFluxErr
1810 return f
'dipDiffFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1812 def _func(self, df):
1818 """Compute the error on the absolute difference of dipole fluxes.
1827 LocalDipoleMeanFluxErr
1829 LocalDipoleDiffFluxErr
1843 return f
'dipDiffFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1845 def _func(self, df):
1854 """Base class for returning the ratio of 2 columns.
1856 Can be used to compute a Signal to Noise ratio for any input flux.
1861 Name of the column to use at the numerator in the ratio
1863 Name of the column to use as the denominator in the ratio.
1879 return f
'ratio_{self.numerator}_{self.denominator}'
1881 def _func(self, df):
1882 with np.warnings.catch_warnings():
1883 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1884 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
std::vector< SchemaItem< Flag > > * items
Point in an unspecified spherical coordinate system.
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 __init__(self, ra, decl, **kwargs)
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)
HtmPixelization provides HTM indexing of points and regions.
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)