24from itertools
import product
29import astropy.units
as u
30from dustmaps.sfd
import SFDQuery
31from astropy.coordinates
import SkyCoord
34from lsst.daf.butler
import DeferredDatasetHandle
38from .parquetTable
import ParquetTable, MultilevelParquetTable
42 typeKey='functor', name=None):
43 """Initialize an object defined in a dictionary
45 The object needs to be importable as
46 f
'{basePath}.{initDict[typeKey]}'
47 The positional
and keyword arguments (
if any) are contained
in
48 "args" and "kwargs" entries
in the dictionary, respectively.
49 This
is used
in `functors.CompositeFunctor.from_yaml` to initialize
50 a composite functor
from a specification
in a YAML file.
55 Dictionary describing object
's initialization. Must contain
56 an entry keyed by ``typeKey`` that is the name of the object,
57 relative to ``basePath``.
59 Path relative to module
in which ``initDict[typeKey]``
is defined.
61 Key of ``initDict`` that
is the name of the object
62 (relative to `basePath`).
64 initDict = initDict.copy()
66 pythonType =
doImport(f
'{basePath}.{initDict.pop(typeKey)}')
68 if 'args' in initDict:
69 args = initDict.pop(
'args')
70 if isinstance(args, str):
73 element = pythonType(*args, **initDict)
74 except Exception
as e:
75 message = f
'Error in constructing functor "{name}" of type {pythonType.__name__} with args: {args}'
76 raise type(e)(message, e.args)
81 """Define and execute a calculation on a ParquetTable
83 The `__call__` method accepts either a `ParquetTable` object or a
84 `DeferredDatasetHandle`,
and returns the
85 result of the calculation
as a single column. Each functor defines what
86 columns are needed
for the calculation,
and only these columns are read
87 from the `ParquetTable`.
89 The action of `__call__` consists of two steps: first, loading the
90 necessary columns
from disk into memory
as a `pandas.DataFrame` object;
91 and second, performing the computation on this dataframe
and returning the
95 To define a new `Functor`, a subclass must define a `_func` method,
96 that takes a `pandas.DataFrame`
and returns result
in a `pandas.Series`.
97 In addition, it must define the following attributes
99 * `_columns`: The columns necessary to perform the calculation
100 * `name`: A name appropriate
for a figure axis label
101 * `shortname`: A name appropriate
for use
as a dictionary key
103 On initialization, a `Functor` should declare what band (`filt` kwarg)
104 and dataset (e.g. `
'ref'`, `
'meas'`, `
'forced_src'`) it
is intended to be
105 applied to. This enables the `_get_data` method to extract the proper
106 columns
from the parquet file. If
not specified, the dataset will fall back
107 on the `_defaultDataset`attribute. If band
is not specified
and `dataset`
108 is anything other than `
'ref'`, then an error will be raised when trying to
109 perform the calculation.
111 Originally, `Functor` was set up to expect
112 datasets formatted like the `deepCoadd_obj` dataset; that
is, a
113 dataframe
with a multi-level column index,
with the levels of the
114 column index being `band`, `dataset`,
and `column`.
115 It has since been generalized to apply to dataframes without mutli-level
116 indices
and multi-level indices
with just `dataset`
and `column` levels.
117 In addition, the `_get_data` method that reads
118 the dataframe
from the `ParquetTable` will
return a dataframe
with column
119 index levels defined by the `_dfLevels` attribute; by default, this
is
122 The `_dfLevels` attributes should generally
not need to
123 be changed, unless `_func` needs columns
from multiple filters
or datasets
124 to do the calculation.
126 which `_dfLevels = (
'band',
'column')`,
and `_func` expects the dataframe
127 it gets to have those levels
in the column index.
132 Filter upon which to do the calculation
135 Dataset upon which to do the calculation
136 (e.g.,
'ref',
'meas',
'forced_src').
140 _defaultDataset = 'ref'
141 _dfLevels = (
'column',)
142 _defaultNoDup =
False
144 def __init__(self, filt=None, dataset=None, noDup=None):
151 if self.
_noDup_noDup
is not None:
158 """Columns required to perform calculation
160 if not hasattr(self,
'_columns'):
161 raise NotImplementedError(
'Must define columns property or _columns attribute')
164 def _get_data_columnLevels(self, data, columnIndex=None):
165 """Gets the names of the column index levels
167 This should only be called in the context of a multilevel table.
168 The logic here
is to enable this to work both
with the gen2 `MultilevelParquetTable`
169 and with the gen3 `DeferredDatasetHandle`.
173 data : `MultilevelParquetTable`
or `DeferredDatasetHandle`
175 columnnIndex (optional): pandas `Index` object
176 if not passed, then it
is read
from the `DeferredDatasetHandle`
178 if isinstance(data, DeferredDatasetHandle):
179 if columnIndex
is None:
180 columnIndex = data.get(component=
"columns")
181 if columnIndex
is not None:
182 return columnIndex.names
183 if isinstance(data, MultilevelParquetTable):
184 return data.columnLevels
186 raise TypeError(f
"Unknown type for data: {type(data)}!")
188 def _get_data_columnLevelNames(self, data, columnIndex=None):
189 """Gets the content of each of the column levels for a multilevel table
191 Similar to `_get_data_columnLevels`, this enables backward compatibility with gen2.
195 if isinstance(data, DeferredDatasetHandle):
196 if columnIndex
is None:
197 columnIndex = data.get(component=
"columns")
198 if columnIndex
is not None:
199 columnLevels = columnIndex.names
201 level:
list(np.unique(np.array([c
for c
in columnIndex])[:, i]))
202 for i, level
in enumerate(columnLevels)
204 return columnLevelNames
205 if isinstance(data, MultilevelParquetTable):
206 return data.columnLevelNames
208 raise TypeError(f
"Unknown type for data: {type(data)}!")
210 def _colsFromDict(self, colDict, columnIndex=None):
211 """Converts dictionary column specficiation to a list of columns
218 for i, lev
in enumerate(columnLevels):
220 if isinstance(colDict[lev], str):
221 new_colDict[lev] = [colDict[lev]]
223 new_colDict[lev] = colDict[lev]
225 new_colDict[lev] = columnIndex.levels[i]
227 levelCols = [new_colDict[lev]
for lev
in columnLevels]
228 cols =
list(product(*levelCols))
229 colsAvailable = [col
for col
in cols
if col
in columnIndex]
233 """Returns columns needed by functor from multilevel dataset
235 To access tables with multilevel column structure, the `MultilevelParquetTable`
236 or `DeferredDatasetHandle` need to be passed either a list of tuples
or a
241 data : `MultilevelParquetTable`
or `DeferredDatasetHandle`
243 columnIndex (optional): pandas `Index` object
244 either passed
or read
in from `DeferredDatasetHandle`.
247 If true, then
return a list of tuples rather than the column dictionary
248 specification. This
is set to `
True` by `CompositeFunctor`
in order to be able to
249 combine columns
from the various component functors.
252 if isinstance(data, DeferredDatasetHandle)
and columnIndex
is None:
253 columnIndex = data.get(component=
"columns")
258 columnDict = {
'column': self.
columnscolumns,
259 'dataset': self.
datasetdataset}
260 if self.
filtfilt
is None:
262 if "band" in columnLevels:
263 if self.
datasetdataset ==
"ref":
264 columnDict[
"band"] = columnLevelNames[
"band"][0]
266 raise ValueError(f
"'filt' not set for functor {self.name}"
267 f
"(dataset {self.dataset}) "
269 "contains multiple filters in column index. "
270 "Set 'filt' or set 'dataset' to 'ref'.")
272 columnDict[
'band'] = self.
filtfilt
274 if isinstance(data, MultilevelParquetTable):
275 return data._colsFromDict(columnDict)
276 elif isinstance(data, DeferredDatasetHandle):
278 return self.
_colsFromDict_colsFromDict(columnDict, columnIndex=columnIndex)
282 def _func(self, df, dropna=True):
283 raise NotImplementedError(
'Must define calculation on dataframe')
285 def _get_columnIndex(self, data):
286 """Return columnIndex
289 if isinstance(data, DeferredDatasetHandle):
290 return data.get(component=
"columns")
294 def _get_data(self, data):
295 """Retrieve dataframe necessary for calculation.
297 The data argument can be a DataFrame, a ParquetTable instance, or a gen3 DeferredDatasetHandle
299 Returns dataframe upon which `self.
_func_func` can act.
301 N.B.
while passing a raw pandas `DataFrame` *should* work here, it has
not been tested.
303 if isinstance(data, pd.DataFrame):
308 is_multiLevel = isinstance(data, MultilevelParquetTable)
or isinstance(columnIndex, pd.MultiIndex)
311 if isinstance(data, ParquetTable)
and not is_multiLevel:
313 df = data.toDataFrame(columns=columns)
322 if isinstance(data, MultilevelParquetTable):
324 df = data.toDataFrame(columns=columns, droplevels=
False)
325 elif isinstance(data, DeferredDatasetHandle):
327 df = data.get(parameters={
"columns": columns})
335 def _setLevels(self, df):
336 levelsToDrop = [n
for n
in df.columns.names
if n
not in self.
_dfLevels_dfLevels]
337 df.columns = df.columns.droplevel(levelsToDrop)
340 def _dropna(self, vals):
346 vals = self.
_func_func(df)
348 vals = self.
failfail(df)
350 vals = self.
_dropna_dropna(vals)
355 """Computes difference between functor called on two different ParquetTable objects
357 return self(data1, **kwargs) - self(data2, **kwargs)
360 return pd.Series(np.full(len(df), np.nan), index=df.index)
364 """Full name of functor (suitable for figure labels)
366 return NotImplementedError
370 """Short name of functor (suitable for column name/dict key)
376 """Perform multiple calculations at once on a catalog
378 The role of a `CompositeFunctor` is to group together computations
from
379 multiple functors. Instead of returning `pandas.Series` a
380 `CompositeFunctor` returns a `pandas.Dataframe`,
with the column names
381 being the keys of `funcDict`.
383 The `columns` attribute of a `CompositeFunctor`
is the union of all columns
384 in all the component functors.
386 A `CompositeFunctor` does
not use a `_func` method itself; rather,
387 when a `CompositeFunctor`
is called, all its columns are loaded
388 at once,
and the resulting dataframe
is passed to the `_func` method of each component
389 functor. This has the advantage of only doing I/O (reading
from parquet file) once,
390 and works because each individual `_func` method of each component functor does
not
391 care
if there are *extra* columns
in the dataframe being passed; only that it must contain
392 *at least* the `columns` it expects.
394 An important
and useful
class method is `from_yaml`, which takes
as argument the path to a YAML
395 file specifying a collection of functors.
399 funcs : `dict`
or `list`
400 Dictionary
or list of functors. If a list, then it will be converted
401 into a dictonary according to the `.shortname` attribute of each functor.
408 if type(funcs) == dict:
411 self.
funcDictfuncDict = {f.shortname: f
for f
in funcs}
413 self.
_filt_filt =
None
419 return self.
_filt_filt
426 self.
_filt_filt = filt
429 if isinstance(new, dict):
431 elif isinstance(new, CompositeFunctor):
434 raise TypeError(
'Can only update with dictionary or CompositeFunctor.')
442 return list(
set([x
for y
in [f.columns
for f
in self.
funcDictfuncDict.values()]
for x
in y]))
451 f.multilevelColumns(data, returnTuple=
True, **kwargs)
for f
in self.
funcDictfuncDict.values()
459 """Apply the functor to the data table
463 data : `lsst.daf.butler.DeferredDatasetHandle`,
466 or `pandas.DataFrame`.
467 The table
or a pointer to a table on disk
from which columns can
473 is_multiLevel = isinstance(data, MultilevelParquetTable)
or isinstance(columnIndex, pd.MultiIndex)
479 if isinstance(data, MultilevelParquetTable):
481 df = data.toDataFrame(columns=columns, droplevels=
False)
482 elif isinstance(data, DeferredDatasetHandle):
484 df = data.get(parameters={
"columns": columns})
489 subdf = f._setLevels(
490 df[f.multilevelColumns(data, returnTuple=
True, columnIndex=columnIndex)]
492 valDict[k] = f._func(subdf)
493 except Exception
as e:
495 valDict[k] = f.fail(subdf)
500 if isinstance(data, DeferredDatasetHandle):
503 elif isinstance(data, pd.DataFrame):
510 valDict = {k: f._func(df)
for k, f
in self.
funcDictfuncDict.
items()}
513 for name, colVal
in valDict.items():
514 if len(colVal.shape) != 1:
515 raise RuntimeError(
"Transformed column '%s' is not the shape of a column. "
516 "It is shaped %s and type %s." % (name, colVal.shape,
type(colVal)))
519 valDf = pd.concat(valDict, axis=1)
521 print([(k,
type(v))
for k, v
in valDict.items()])
524 if kwargs.get(
'dropna',
False):
525 valDf = valDf.dropna(how=
'any')
531 if renameRules
is None:
533 for old, new
in renameRules:
534 if col.startswith(old):
535 col = col.replace(old, new)
541 filename = os.path.expandvars(filename)
542 with open(filename)
as f:
543 translationDefinition = yaml.safe_load(f)
545 return cls.
from_yamlfrom_yaml(translationDefinition, **kwargs)
550 for func, val
in translationDefinition[
'funcs'].
items():
553 if 'flag_rename_rules' in translationDefinition:
554 renameRules = translationDefinition[
'flag_rename_rules']
558 if 'calexpFlags' in translationDefinition:
559 for flag
in translationDefinition[
'calexpFlags']:
560 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'calexp')
562 if 'refFlags' in translationDefinition:
563 for flag
in translationDefinition[
'refFlags']:
564 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'ref')
566 if 'forcedFlags' in translationDefinition:
567 for flag
in translationDefinition[
'forcedFlags']:
568 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'forced_src')
570 if 'flags' in translationDefinition:
571 for flag
in translationDefinition[
'flags']:
572 funcs[cls.
renameColrenameCol(flag, renameRules)] =
Column(flag, dataset=
'meas')
574 return cls(funcs, **kwargs)
578 """Evaluate an expression on a DataFrame, knowing what the 'mag' function means
580 Builds on `pandas.DataFrame.eval`, which parses and executes math on dataframes.
584 df : pandas.DataFrame
585 Dataframe on which to evaluate expression.
591 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>)/log(10)', expr)
592 val = df.eval(expr_new)
594 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>_instFlux)/log(10)', expr)
595 val = df.eval(expr_new)
600 """Arbitrary computation on a catalog
602 Column names (and thus the columns to be loaded
from catalog) are found
603 by finding all words
and trying to ignore all
"math-y" words.
608 Expression to evaluate, to be parsed
and executed by `mag_aware_eval`.
610 _ignore_words = ('mag',
'sin',
'cos',
'exp',
'log',
'sqrt')
622 flux_cols = re.findall(
r'mag\(\s*(\w+)\s*\)', self.
exprexpr)
624 cols = [c
for c
in re.findall(
r'[a-zA-Z_]+', self.
exprexpr)
if c
not in self.
_ignore_words_ignore_words]
627 if not re.search(
'_instFlux$', c):
628 cols.append(f
'{c}_instFlux')
633 return list(
set([c
for c
in cols
if c
not in not_a_col]))
640 """Get column with specified name
656 return df[self.
colcol]
660 """Return the value of the index for each object
663 columns = ['coord_ra']
664 _defaultDataset =
'ref'
668 return pd.Series(df.index, index=df.index)
673 _allow_difference =
False
677 return pd.Series(df.index, index=df.index)
681 col =
'base_Footprint_nPix'
685 """Base class for coordinate column, in degrees
694 output = df[self.
colcol] * 180 / np.pi
if self.
_radians_radians
else df[self.
colcol]
699 """Right Ascension, in degrees
705 super().
__init__(
'coord_ra', **kwargs)
708 return super().
__call__(catalog, **kwargs)
712 """Declination, in degrees
718 super().
__init__(
'coord_dec', **kwargs)
721 return super().
__call__(catalog, **kwargs)
725 """Compute the level 20 HtmIndex for the catalog.
729 This functor was implemented to satisfy requirements of old APDB interface
730 which required ``pixelId`` column in DiaObject
with HTM20 index. APDB
731 interface had migrated to
not need that information, but we keep this
732 class in case it may be useful for something else.
747 def computePixel(row):
756 return self.
pixelatorpixelator.index(sphPoint.getVector())
758 return df.apply(computePixel, axis=1, result_type=
'reduce').astype(
'int64')
762 if not col.endswith(
'_instFlux'):
768 if not col.endswith(
'_instFluxErr'):
769 col +=
'_instFluxErr'
774 """Compute calibrated magnitude
776 Takes a `calib` argument, which returns the flux at mag=0
777 as `calib.getFluxMag0()`. If
not provided, then the default
778 `fluxMag0`
is 63095734448.0194, which
is default
for HSC.
779 This default should be removed
in DM-21955
781 This calculation hides warnings about invalid values
and dividing by zero.
783 As
for all functors, a `dataset`
and `filt` kwarg should be provided upon
784 initialization. Unlike the default `Functor`, however, the default dataset
785 for a `Mag`
is `
'meas'`, rather than `
'ref'`.
790 Name of flux column
from which to compute magnitude. Can be parseable
791 by `lsst.pipe.tasks.functors.fluxName` function---that
is, you can
pass
792 `
'modelfit_CModel'` instead of `
'modelfit_CModel_instFlux'`)
and it will
794 calib : `lsst.afw.image.calib.Calib` (optional)
795 Object that knows zero point.
797 _defaultDataset = 'meas'
802 if calib
is not None:
806 self.
fluxMag0fluxMag0 = 63095734448.0194
815 with np.warnings.catch_warnings():
816 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
817 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
818 return -2.5*np.log10(df[self.
colcol] / self.
fluxMag0fluxMag0)
822 return f
'mag_{self.col}'
826 """Compute calibrated magnitude uncertainty
833 calib : `lsst.afw.image.calib.Calib` (optional)
834 Object that knows zero point.
839 if self.
calibcalib
is not None:
846 return [self.
colcol, self.
colcol +
'Err']
849 with np.warnings.catch_warnings():
850 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
851 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
853 x = df[fluxErrCol] / df[fluxCol]
855 magErr = (2.5 / np.log(10.)) * np.sqrt(x*x + y*y)
860 return super().name +
'_err'
868 return (df[self.
colcol] / self.
fluxMag0fluxMag0) * 1e9
872 _defaultDataset =
'meas'
874 """Functor to calculate magnitude difference"""
883 return [self.
col1col1, self.
col2col2]
886 with np.warnings.catch_warnings():
887 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
888 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
889 return -2.5*np.log10(df[self.
col1col1]/df[self.
col2col2])
893 return f
'(mag_{self.col1} - mag_{self.col2})'
897 return f
'magDiff_{self.col1}_{self.col2}'
901 """Compute the color between two filters
903 Computes color by initializing two different `Mag`
904 functors based on the `col` and filters provided,
and
905 then returning the difference.
907 This
is enabled by the `_func` expecting a dataframe
with a
908 multilevel column index,
with both `
'band'`
and `
'column'`,
909 instead of just `
'column'`, which
is the `Functor` default.
910 This
is controlled by the `_dfLevels` attribute.
912 Also of note, the default dataset
for `Color`
is `forced_src
'`,
913 whereas for `Mag` it
is `
'meas'`.
918 Name of flux column
from which to compute; same
as would be passed to
922 Filters
from which to compute magnitude difference.
923 Color computed
is `
Mag(filt2) -
Mag(filt1)`.
925 _defaultDataset = 'forced_src'
926 _dfLevels = (
'band',
'column')
932 raise RuntimeError(
"Cannot compute Color for %s: %s - %s " % (col, filt2, filt1))
936 self.
mag2mag2 =
Mag(col, filt=filt2, **kwargs)
937 self.
mag1mag1 =
Mag(col, filt=filt1, **kwargs)
950 mag2 = self.mag2._func(df[self.filt2])
951 mag1 = self.mag1._func(df[self.filt1])
956 return [self.
mag1mag1.col, self.
mag2mag2.col]
963 return f
'{self.filt2} - {self.filt1} ({self.col})'
967 return f
"{self.col}_{self.filt2.replace('-', '')}m{self.filt1.replace('-', '')}"
971 """Main function of this subclass is to override the dropna=True
974 _allow_difference =
False
979 return super().
__call__(parq, dropna=
False, **kwargs)
983 _columns = [
"base_ClassificationExtendedness_value"]
984 _column =
"base_ClassificationExtendedness_value"
989 test = (x < 0.5).astype(int)
990 test = test.mask(mask, 2)
994 categories = [
'galaxy',
'star', self.
_null_label_null_label]
995 label = pd.Series(pd.Categorical.from_codes(test, categories=categories),
996 index=x.index, name=
'label')
998 label = label.astype(str)
1003 _columns = [
'numStarFlags']
1004 labels = {
"star": 0,
"maybe": 1,
"notStar": 2}
1006 def _func(self, df):
1010 n = len(x.unique()) - 1
1012 labels = [
'noStar',
'maybe',
'star']
1013 label = pd.Series(pd.cut(x, [-1, 0, n-1, n], labels=labels),
1014 index=x.index, name=
'label')
1017 label = label.astype(str)
1023 name =
'Deconvolved Moments'
1024 shortname =
'deconvolvedMoments'
1025 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1026 "ext_shapeHSM_HsmSourceMoments_yy",
1027 "base_SdssShape_xx",
"base_SdssShape_yy",
1028 "ext_shapeHSM_HsmPsfMoments_xx",
1029 "ext_shapeHSM_HsmPsfMoments_yy")
1031 def _func(self, df):
1032 """Calculate deconvolved moments"""
1033 if "ext_shapeHSM_HsmSourceMoments_xx" in df.columns:
1034 hsm = df[
"ext_shapeHSM_HsmSourceMoments_xx"] + df[
"ext_shapeHSM_HsmSourceMoments_yy"]
1036 hsm = np.ones(len(df))*np.nan
1037 sdss = df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]
1038 if "ext_shapeHSM_HsmPsfMoments_xx" in df.columns:
1039 psf = df[
"ext_shapeHSM_HsmPsfMoments_xx"] + df[
"ext_shapeHSM_HsmPsfMoments_yy"]
1044 raise RuntimeError(
'No psf shape parameter found in catalog')
1046 return hsm.where(np.isfinite(hsm), sdss) - psf
1050 """Functor to calculate SDSS trace radius size for sources"""
1051 name =
"SDSS Trace Size"
1052 shortname =
'sdssTrace'
1053 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy")
1055 def _func(self, df):
1056 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1061 """Functor to calculate SDSS trace radius size difference (%) between object and psf model"""
1062 name =
"PSF - SDSS Trace Size"
1063 shortname =
'psf_sdssTrace'
1064 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy",
1065 "base_SdssShape_psf_xx",
"base_SdssShape_psf_yy")
1067 def _func(self, df):
1068 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1069 psfSize = np.sqrt(0.5*(df[
"base_SdssShape_psf_xx"] + df[
"base_SdssShape_psf_yy"]))
1070 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1075 """Functor to calculate HSM trace radius size for sources"""
1076 name =
'HSM Trace Size'
1077 shortname =
'hsmTrace'
1078 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1079 "ext_shapeHSM_HsmSourceMoments_yy")
1081 def _func(self, df):
1082 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1083 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1088 """Functor to calculate HSM trace radius size difference (%) between object and psf model"""
1089 name =
'PSF - HSM Trace Size'
1090 shortname =
'psf_HsmTrace'
1091 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1092 "ext_shapeHSM_HsmSourceMoments_yy",
1093 "ext_shapeHSM_HsmPsfMoments_xx",
1094 "ext_shapeHSM_HsmPsfMoments_yy")
1096 def _func(self, df):
1097 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1098 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1099 psfSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmPsfMoments_xx"]
1100 + df[
"ext_shapeHSM_HsmPsfMoments_yy"]))
1101 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1106 name =
'HSM Psf FWHM'
1107 _columns = (
'ext_shapeHSM_HsmPsfMoments_xx',
'ext_shapeHSM_HsmPsfMoments_yy')
1110 SIGMA2FWHM = 2*np.sqrt(2*np.log(2))
1112 def _func(self, df):
1114 0.5*(df[
'ext_shapeHSM_HsmPsfMoments_xx'] + df[
'ext_shapeHSM_HsmPsfMoments_yy']))
1118 name =
"Distortion Ellipticity (e1)"
1119 shortname =
"Distortion"
1132 def _func(self, df):
1137 name =
"Ellipticity e2"
1149 def _func(self, df):
1150 return 2*df[self.
colXYcolXY] / (df[self.
colXXcolXX] + df[self.
colYYcolYY])
1165 def _func(self, df):
1166 return (df[self.
colXXcolXX]*df[self.
colYYcolYY] - df[self.
colXYcolXY]**2)**0.25
1170 """Computations using the stored localWcs.
1172 name = "LocalWcsOperations"
1187 """Compute the distance on the sphere from x2, y1 to x1, y1.
1195 cd11 : `pandas.Series`
1196 [1, 1] element of the local Wcs affine transform.
1197 cd11 : `pandas.Series`
1198 [1, 1] element of the local Wcs affine transform.
1199 cd12 : `pandas.Series`
1200 [1, 2] element of the local Wcs affine transform.
1201 cd21 : `pandas.Series`
1202 [2, 1] element of the local Wcs affine transform.
1203 cd22 : `pandas.Series`
1204 [2, 2] element of the local Wcs affine transform.
1209 RA and dec conversion of x
and y given the local Wcs. Returned
1210 units are
in radians.
1213 return (x * cd11 + y * cd12, x * cd21 + y * cd22)
1216 """Compute the local pixel scale conversion.
1220 ra1 : `pandas.Series`
1221 Ra of the first coordinate in radians.
1222 dec1 : `pandas.Series`
1223 Dec of the first coordinate
in radians.
1224 ra2 : `pandas.Series`
1225 Ra of the second coordinate
in radians.
1226 dec2 : `pandas.Series`
1227 Dec of the second coordinate
in radians.
1231 dist : `pandas.Series`
1232 Distance on the sphere
in radians.
1234 deltaDec = dec2 - dec1
1236 return 2 * np.arcsin(
1238 np.sin(deltaDec / 2) ** 2
1239 + np.cos(dec2) * np.cos(dec1) * np.sin(deltaRa / 2) ** 2))
1242 """Compute the distance on the sphere from x2, y1 to x1, y1.
1246 x1 : `pandas.Series`
1248 y1 : `pandas.Series`
1250 x2 : `pandas.Series`
1252 y2 : `pandas.Series`
1254 cd11 : `pandas.Series`
1255 [1, 1] element of the local Wcs affine transform.
1256 cd11 : `pandas.Series`
1257 [1, 1] element of the local Wcs affine transform.
1258 cd12 : `pandas.Series`
1259 [1, 2] element of the local Wcs affine transform.
1260 cd21 : `pandas.Series`
1261 [2, 1] element of the local Wcs affine transform.
1262 cd22 : `pandas.Series`
1263 [2, 2] element of the local Wcs affine transform.
1267 Distance : `pandas.Series`
1268 Arcseconds per pixel at the location of the local WC
1270 ra1, dec1 = self.computeDeltaRaDeccomputeDeltaRaDec(x1, y1, cd11, cd12, cd21, cd22)
1271 ra2, dec2 = self.computeDeltaRaDeccomputeDeltaRaDec(x2, y2, cd11, cd12, cd21, cd22)
1277 """Compute the local pixel scale from the stored CDMatrix.
1289 """Compute the local pixel to scale conversion in arcseconds.
1293 cd11 : `pandas.Series`
1294 [1, 1] element of the local Wcs affine transform in radians.
1295 cd11 : `pandas.Series`
1296 [1, 1] element of the local Wcs affine transform
in radians.
1297 cd12 : `pandas.Series`
1298 [1, 2] element of the local Wcs affine transform
in radians.
1299 cd21 : `pandas.Series`
1300 [2, 1] element of the local Wcs affine transform
in radians.
1301 cd22 : `pandas.Series`
1302 [2, 2] element of the local Wcs affine transform
in radians.
1306 pixScale : `pandas.Series`
1307 Arcseconds per pixel at the location of the local WC
1309 return 3600 * np.degrees(np.sqrt(np.fabs(cd11 * cd22 - cd12 * cd21)))
1311 def _func(self, df):
1319 """Convert a value in units pixels squared to units arcseconds squared.
1338 return f
"{self.col}_asArcseconds"
1342 return [self.
colcol,
1348 def _func(self, df):
1356 """Convert a value in units pixels to units arcseconds.
1375 return f
"{self.col}_asArcsecondsSq"
1379 return [self.
colcol,
1385 def _func(self, df):
1390 return df[self.
colcol] * pixScale * pixScale
1394 name =
'Reference Band'
1395 shortname =
'refBand'
1399 return [
"merge_measurement_i",
1400 "merge_measurement_r",
1401 "merge_measurement_z",
1402 "merge_measurement_y",
1403 "merge_measurement_g",
1404 "merge_measurement_u"]
1406 def _func(self, df: pd.DataFrame) -> pd.Series:
1407 def getFilterAliasName(row):
1409 colName = row.idxmax()
1410 return colName.replace(
'merge_measurement_',
'')
1414 columns = [col
for col
in self.
columnscolumnscolumns
if col
in df.columns]
1416 return df[columns].apply(getFilterAliasName, axis=1,
1417 result_type=
'reduce').astype(
'object')
1422 AB_FLUX_SCALE = (0 * u.ABmag).to_value(u.nJy)
1423 LOG_AB_FLUX_SCALE = 12.56
1424 FIVE_OVER_2LOG10 = 1.085736204758129569
1428 def __init__(self, colFlux, colFluxErr=None, calib=None, **kwargs):
1434 if calib
is not None:
1444 return [self.
colcol]
1448 return f
'mag_{self.col}'
1452 if np.abs(a) < np.abs(b):
1457 return np.abs(a) * np.sqrt(1. + q*q)
1463 with np.warnings.catch_warnings():
1464 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1465 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
1466 return -2.5 * np.log10(dn/fluxMag0)
1469 retVal = self.
vhypotvhypot(dn * fluxMag0Err, dnErr * fluxMag0)
1470 retVal *= self.
AB_FLUX_SCALEAB_FLUX_SCALE / fluxMag0 / fluxMag0
1474 retVal = self.
dn2fluxErrdn2fluxErr(dn, dnErr, fluxMag0, fluxMag0Err) / self.
dn2fluxdn2flux(dn, fluxMag0)
1479 def _func(self, df):
1488 def _func(self, df):
1490 return pd.Series(retArr, index=df.index)
1494 def _func(self, df):
1503 def _func(self, df):
1505 return pd.Series(retArr, index=df.index)
1509 """Base class for calibrating the specified instrument flux column using
1510 the local photometric calibration.
1515 Name of the instrument flux column.
1516 instFluxErrCol : `str`
1517 Name of the assocated error columns for ``instFluxCol``.
1518 photoCalibCol : `str`
1519 Name of local calibration column.
1520 photoCalibErrCol : `str`
1521 Error associated
with ``photoCalibCol``
1531 logNJanskyToAB = (1 * u.nJy).to_value(u.ABmag)
1546 """Convert instrument flux to nanojanskys.
1550 instFlux : `numpy.ndarray` or `pandas.Series`
1551 Array of instrument flux measurements
1552 localCalib : `numpy.ndarray`
or `pandas.Series`
1553 Array of local photometric calibration estimates.
1557 calibFlux : `numpy.ndarray`
or `pandas.Series`
1558 Array of calibrated flux measurements.
1560 return instFlux * localCalib
1563 """Convert instrument flux to nanojanskys.
1567 instFlux : `numpy.ndarray` or `pandas.Series`
1568 Array of instrument flux measurements
1569 instFluxErr : `numpy.ndarray`
or `pandas.Series`
1570 Errors on associated ``instFlux`` values
1571 localCalib : `numpy.ndarray`
or `pandas.Series`
1572 Array of local photometric calibration estimates.
1573 localCalibErr : `numpy.ndarray`
or `pandas.Series`
1574 Errors on associated ``localCalib`` values
1578 calibFluxErr : `numpy.ndarray`
or `pandas.Series`
1579 Errors on calibrated flux measurements.
1581 return np.hypot(instFluxErr * localCalib, instFlux * localCalibErr)
1584 """Convert instrument flux to nanojanskys.
1588 instFlux : `numpy.ndarray` or `pandas.Series`
1589 Array of instrument flux measurements
1590 localCalib : `numpy.ndarray`
or `pandas.Series`
1591 Array of local photometric calibration estimates.
1595 calibMag : `numpy.ndarray`
or `pandas.Series`
1596 Array of calibrated AB magnitudes.
1601 """Convert instrument flux err to nanojanskys.
1605 instFlux : `numpy.ndarray` or `pandas.Series`
1606 Array of instrument flux measurements
1607 instFluxErr : `numpy.ndarray`
or `pandas.Series`
1608 Errors on associated ``instFlux`` values
1609 localCalib : `numpy.ndarray`
or `pandas.Series`
1610 Array of local photometric calibration estimates.
1611 localCalibErr : `numpy.ndarray`
or `pandas.Series`
1612 Errors on associated ``localCalib`` values
1616 calibMagErr: `numpy.ndarray`
or `pandas.Series`
1617 Error on calibrated AB magnitudes.
1620 return 2.5 / np.log(10) * err / self.
instFluxToNanojanskyinstFluxToNanojansky(instFlux, instFluxErr)
1624 """Compute calibrated fluxes using the local calibration value.
1640 return f
'flux_{self.instFluxCol}'
1642 def _func(self, df):
1647 """Compute calibrated flux errors using the local calibration value.
1664 return f
'fluxErr_{self.instFluxCol}'
1666 def _func(self, df):
1672 """Compute calibrated AB magnitudes using the local calibration value.
1688 return f
'mag_{self.instFluxCol}'
1690 def _func(self, df):
1696 """Compute calibrated AB magnitude errors using the local calibration value.
1713 return f
'magErr_{self.instFluxCol}'
1715 def _func(self, df):
1723 """Compute absolute mean of dipole fluxes.
1732 LocalDipoleMeanFluxErr
1734 LocalDipoleDiffFluxErr
1764 return f
'dipMeanFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1766 def _func(self, df):
1772 """Compute the error on the absolute mean of dipole fluxes.
1781 LocalDipoleMeanFluxErr
1783 LocalDipoleDiffFluxErr
1797 return f
'dipMeanFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1799 def _func(self, df):
1808 """Compute the absolute difference of dipole fluxes.
1810 Value is (
abs(pos) -
abs(neg))
1819 LocalDipoleMeanFluxErr
1821 LocalDipoleDiffFluxErr
1832 return f
'dipDiffFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1834 def _func(self, df):
1840 """Compute the error on the absolute difference of dipole fluxes.
1849 LocalDipoleMeanFluxErr
1851 LocalDipoleDiffFluxErr
1865 return f
'dipDiffFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1867 def _func(self, df):
1876 """Base class for returning the ratio of 2 columns.
1878 Can be used to compute a Signal to Noise ratio for any input flux.
1883 Name of the column to use at the numerator
in the ratio
1885 Name of the column to use
as the denominator
in the ratio.
1901 return f
'ratio_{self.numerator}_{self.denominator}'
1903 def _func(self, df):
1904 with np.warnings.catch_warnings():
1905 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1906 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
1911 """Compute E(B-V) from dustmaps.sfd
1913 _defaultDataset = 'ref'
1918 self.
_columns_columns = [
'coord_ra',
'coord_dec']
1922 def _func(self, df):
1923 coords = SkyCoord(df[
'coord_ra']*u.rad, df[
'coord_dec']*u.rad)
1924 ebv = self.
sfdsfd(coords)
1927 return pd.Series(ebv, index=df.index).astype(
'float64')
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 __init__(self, **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)
Angle abs(Angle const &a)