22__all__ = [
"Functor",
"CompositeFunctor",
"CustomFunctor",
"Column",
"Index",
23 "IDColumn",
"FootprintNPix",
"CoordColumn",
"RAColumn",
"DecColumn",
24 "HtmIndex20",
"Mag",
"MagErr",
"NanoMaggie",
"MagDiff",
"Color",
25 "Labeller",
"StarGalaxyLabeller",
"NumStarLabeller",
"DeconvolvedMoments",
26 "SdssTraceSize",
"PsfSdssTraceSizeDiff",
"HsmTraceSize",
"PsfHsmTraceSizeDiff",
27 "HsmFwhm",
"E1",
"E2",
"RadiusFromQuadrupole",
"LocalWcs",
"ComputePixelScale",
28 "ConvertPixelToArcseconds",
"ConvertPixelSqToArcsecondsSq",
"ReferenceBand",
29 "Photometry",
"NanoJansky",
"NanoJanskyErr",
"Magnitude",
"MagnitudeErr",
30 "LocalPhotometry",
"LocalNanojansky",
"LocalNanojanskyErr",
31 "LocalMagnitude",
"LocalMagnitudeErr",
"LocalDipoleMeanFlux",
32 "LocalDipoleMeanFluxErr",
"LocalDipoleDiffFlux",
"LocalDipoleDiffFluxErr",
37from itertools
import product
43import astropy.units
as u
44from dustmaps.sfd
import SFDQuery
45from astropy.coordinates
import SkyCoord
48from lsst.daf.butler
import DeferredDatasetHandle
52from .parquetTable
import ParquetTable, MultilevelParquetTable
56 typeKey='functor', name=None):
57 """Initialize an object defined in a dictionary
59 The object needs to be importable as
60 f
'{basePath}.{initDict[typeKey]}'
61 The positional
and keyword arguments (
if any) are contained
in
62 "args" and "kwargs" entries
in the dictionary, respectively.
63 This
is used
in `functors.CompositeFunctor.from_yaml` to initialize
64 a composite functor
from a specification
in a YAML file.
69 Dictionary describing object
's initialization. Must contain
70 an entry keyed by ``typeKey`` that is the name of the object,
71 relative to ``basePath``.
73 Path relative to module
in which ``initDict[typeKey]``
is defined.
75 Key of ``initDict`` that
is the name of the object
76 (relative to `basePath`).
78 initDict = initDict.copy()
80 pythonType = doImport(f
'{basePath}.{initDict.pop(typeKey)}')
82 if 'args' in initDict:
83 args = initDict.pop(
'args')
84 if isinstance(args, str):
87 element = pythonType(*args, **initDict)
88 except Exception
as e:
89 message = f
'Error in constructing functor "{name}" of type {pythonType.__name__} with args: {args}'
90 raise type(e)(message, e.args)
95 """Define and execute a calculation on a ParquetTable
97 The `__call__` method accepts either a `ParquetTable` object or a
98 `DeferredDatasetHandle`,
and returns the
99 result of the calculation
as a single column. Each functor defines what
100 columns are needed
for the calculation,
and only these columns are read
101 from the `ParquetTable`.
103 The action of `__call__` consists of two steps: first, loading the
104 necessary columns
from disk into memory
as a `pandas.DataFrame` object;
105 and second, performing the computation on this dataframe
and returning the
109 To define a new `Functor`, a subclass must define a `_func` method,
110 that takes a `pandas.DataFrame`
and returns result
in a `pandas.Series`.
111 In addition, it must define the following attributes
113 * `_columns`: The columns necessary to perform the calculation
114 * `name`: A name appropriate
for a figure axis label
115 * `shortname`: A name appropriate
for use
as a dictionary key
117 On initialization, a `Functor` should declare what band (`filt` kwarg)
118 and dataset (e.g. `
'ref'`, `
'meas'`, `
'forced_src'`) it
is intended to be
119 applied to. This enables the `_get_data` method to extract the proper
120 columns
from the parquet file. If
not specified, the dataset will fall back
121 on the `_defaultDataset`attribute. If band
is not specified
and `dataset`
122 is anything other than `
'ref'`, then an error will be raised when trying to
123 perform the calculation.
125 Originally, `Functor` was set up to expect
126 datasets formatted like the `deepCoadd_obj` dataset; that
is, a
127 dataframe
with a multi-level column index,
with the levels of the
128 column index being `band`, `dataset`,
and `column`.
129 It has since been generalized to apply to dataframes without mutli-level
130 indices
and multi-level indices
with just `dataset`
and `column` levels.
131 In addition, the `_get_data` method that reads
132 the dataframe
from the `ParquetTable` will
return a dataframe
with column
133 index levels defined by the `_dfLevels` attribute; by default, this
is
136 The `_dfLevels` attributes should generally
not need to
137 be changed, unless `_func` needs columns
from multiple filters
or datasets
138 to do the calculation.
140 which `_dfLevels = (
'band',
'column')`,
and `_func` expects the dataframe
141 it gets to have those levels
in the column index.
146 Filter upon which to do the calculation
149 Dataset upon which to do the calculation
150 (e.g.,
'ref',
'meas',
'forced_src').
154 _defaultDataset = 'ref'
155 _dfLevels = (
'column',)
156 _defaultNoDup =
False
158 def __init__(self, filt=None, dataset=None, noDup=None):
162 self.
log = logging.getLogger(
type(self).__name__)
166 if self.
_noDup is not None:
173 """Columns required to perform calculation
175 if not hasattr(self,
'_columns'):
176 raise NotImplementedError(
'Must define columns property or _columns attribute')
179 def _get_data_columnLevels(self, data, columnIndex=None):
180 """Gets the names of the column index levels
182 This should only be called in the context of a multilevel table.
183 The logic here
is to enable this to work both
with the gen2 `MultilevelParquetTable`
184 and with the gen3 `DeferredDatasetHandle`.
188 data : `MultilevelParquetTable`
or `DeferredDatasetHandle`
190 columnnIndex (optional): pandas `Index` object
191 if not passed, then it
is read
from the `DeferredDatasetHandle`
193 if isinstance(data, DeferredDatasetHandle):
194 if columnIndex
is None:
195 columnIndex = data.get(component=
"columns")
196 if columnIndex
is not None:
197 return columnIndex.names
198 if isinstance(data, MultilevelParquetTable):
199 return data.columnLevels
201 raise TypeError(f
"Unknown type for data: {type(data)}!")
203 def _get_data_columnLevelNames(self, data, columnIndex=None):
204 """Gets the content of each of the column levels for a multilevel table
206 Similar to `_get_data_columnLevels`, this enables backward compatibility with gen2.
210 if isinstance(data, DeferredDatasetHandle):
211 if columnIndex
is None:
212 columnIndex = data.get(component=
"columns")
213 if columnIndex
is not None:
214 columnLevels = columnIndex.names
216 level:
list(np.unique(np.array([c
for c
in columnIndex])[:, i]))
217 for i, level
in enumerate(columnLevels)
219 return columnLevelNames
220 if isinstance(data, MultilevelParquetTable):
221 return data.columnLevelNames
223 raise TypeError(f
"Unknown type for data: {type(data)}!")
225 def _colsFromDict(self, colDict, columnIndex=None):
226 """Converts dictionary column specficiation to a list of columns
233 for i, lev
in enumerate(columnLevels):
235 if isinstance(colDict[lev], str):
236 new_colDict[lev] = [colDict[lev]]
238 new_colDict[lev] = colDict[lev]
240 new_colDict[lev] = columnIndex.levels[i]
242 levelCols = [new_colDict[lev]
for lev
in columnLevels]
243 cols =
list(product(*levelCols))
244 colsAvailable = [col
for col
in cols
if col
in columnIndex]
248 """Returns columns needed by functor from multilevel dataset
250 To access tables with multilevel column structure, the `MultilevelParquetTable`
251 or `DeferredDatasetHandle` need to be passed either a list of tuples
or a
256 data : `MultilevelParquetTable`
or `DeferredDatasetHandle`
258 columnIndex (optional): pandas `Index` object
259 either passed
or read
in from `DeferredDatasetHandle`.
262 If true, then
return a list of tuples rather than the column dictionary
263 specification. This
is set to `
True` by `CompositeFunctor`
in order to be able to
264 combine columns
from the various component functors.
267 if isinstance(data, DeferredDatasetHandle)
and columnIndex
is None:
268 columnIndex = data.get(component=
"columns")
273 columnDict = {
'column': self.
columns,
275 if self.
filt is None:
277 if "band" in columnLevels:
279 columnDict[
"band"] = columnLevelNames[
"band"][0]
281 raise ValueError(f
"'filt' not set for functor {self.name}"
282 f
"(dataset {self.dataset}) "
284 "contains multiple filters in column index. "
285 "Set 'filt' or set 'dataset' to 'ref'.")
287 columnDict[
'band'] = self.
filt
289 if isinstance(data, MultilevelParquetTable):
290 return data._colsFromDict(columnDict)
291 elif isinstance(data, DeferredDatasetHandle):
293 return self.
_colsFromDict(columnDict, columnIndex=columnIndex)
297 def _func(self, df, dropna=True):
298 raise NotImplementedError(
'Must define calculation on dataframe')
300 def _get_columnIndex(self, data):
301 """Return columnIndex
304 if isinstance(data, DeferredDatasetHandle):
305 return data.get(component=
"columns")
309 def _get_data(self, data):
310 """Retrieve dataframe necessary for calculation.
312 The data argument can be a DataFrame, a ParquetTable instance, or a gen3 DeferredDatasetHandle
314 Returns dataframe upon which `self.
_func` can act.
316 N.B.
while passing a raw pandas `DataFrame` *should* work here, it has
not been tested.
318 if isinstance(data, pd.DataFrame):
323 is_multiLevel = isinstance(data, MultilevelParquetTable)
or isinstance(columnIndex, pd.MultiIndex)
326 if isinstance(data, ParquetTable)
and not is_multiLevel:
328 df = data.toDataFrame(columns=columns)
337 if isinstance(data, MultilevelParquetTable):
339 df = data.toDataFrame(columns=columns, droplevels=
False)
340 elif isinstance(data, DeferredDatasetHandle):
342 df = data.get(parameters={
"columns": columns})
350 def _setLevels(self, df):
351 levelsToDrop = [n
for n
in df.columns.names
if n
not in self.
_dfLevels]
352 df.columns = df.columns.droplevel(levelsToDrop)
355 def _dropna(self, vals):
361 vals = self.
_func(df)
362 except Exception
as e:
363 self.
log.error(
"Exception in %s call: %s: %s", self.
name,
type(e).__name__, e)
371 """Computes difference between functor called on two different ParquetTable objects
373 return self(data1, **kwargs) - self(data2, **kwargs)
376 return pd.Series(np.full(len(df), np.nan), index=df.index)
380 """Full name of functor (suitable for figure labels)
382 return NotImplementedError
386 """Short name of functor (suitable for column name/dict key)
392 """Perform multiple calculations at once on a catalog
394 The role of a `CompositeFunctor` is to group together computations
from
395 multiple functors. Instead of returning `pandas.Series` a
396 `CompositeFunctor` returns a `pandas.Dataframe`,
with the column names
397 being the keys of `funcDict`.
399 The `columns` attribute of a `CompositeFunctor`
is the union of all columns
400 in all the component functors.
402 A `CompositeFunctor` does
not use a `_func` method itself; rather,
403 when a `CompositeFunctor`
is called, all its columns are loaded
404 at once,
and the resulting dataframe
is passed to the `_func` method of each component
405 functor. This has the advantage of only doing I/O (reading
from parquet file) once,
406 and works because each individual `_func` method of each component functor does
not
407 care
if there are *extra* columns
in the dataframe being passed; only that it must contain
408 *at least* the `columns` it expects.
410 An important
and useful
class method is `from_yaml`, which takes
as argument the path to a YAML
411 file specifying a collection of functors.
415 funcs : `dict`
or `list`
416 Dictionary
or list of functors. If a list, then it will be converted
417 into a dictonary according to the `.shortname` attribute of each functor.
424 if type(funcs) == dict:
427 self.
funcDict = {f.shortname: f
for f
in funcs}
445 if isinstance(new, dict):
447 elif isinstance(new, CompositeFunctor):
450 raise TypeError(
'Can only update with dictionary or CompositeFunctor.')
458 return list(
set([x
for y
in [f.columns
for f
in self.
funcDict.values()]
for x
in y]))
467 f.multilevelColumns(data, returnTuple=
True, **kwargs)
for f
in self.
funcDict.values()
475 """Apply the functor to the data table
479 data : `lsst.daf.butler.DeferredDatasetHandle`,
482 or `pandas.DataFrame`.
483 The table
or a pointer to a table on disk
from which columns can
489 is_multiLevel = isinstance(data, MultilevelParquetTable)
or isinstance(columnIndex, pd.MultiIndex)
495 if isinstance(data, MultilevelParquetTable):
497 df = data.toDataFrame(columns=columns, droplevels=
False)
498 elif isinstance(data, DeferredDatasetHandle):
500 df = data.get(parameters={
"columns": columns})
505 subdf = f._setLevels(
506 df[f.multilevelColumns(data, returnTuple=
True, columnIndex=columnIndex)]
508 valDict[k] = f._func(subdf)
509 except Exception
as e:
510 self.
log.error(
"Exception in %s call: %s: %s", self.
name,
type(e).__name__, e)
512 valDict[k] = f.fail(subdf)
517 if isinstance(data, DeferredDatasetHandle):
520 elif isinstance(data, pd.DataFrame):
530 for name, colVal
in valDict.items():
531 if len(colVal.shape) != 1:
532 raise RuntimeError(
"Transformed column '%s' is not the shape of a column. "
533 "It is shaped %s and type %s." % (name, colVal.shape,
type(colVal)))
536 valDf = pd.concat(valDict, axis=1)
538 print([(k,
type(v))
for k, v
in valDict.items()])
541 if kwargs.get(
'dropna',
False):
542 valDf = valDf.dropna(how=
'any')
548 if renameRules
is None:
550 for old, new
in renameRules:
551 if col.startswith(old):
552 col = col.replace(old, new)
558 filename = os.path.expandvars(filename)
559 with open(filename)
as f:
560 translationDefinition = yaml.safe_load(f)
562 return cls.
from_yaml(translationDefinition, **kwargs)
567 for func, val
in translationDefinition[
'funcs'].
items():
570 if 'flag_rename_rules' in translationDefinition:
571 renameRules = translationDefinition[
'flag_rename_rules']
575 if 'calexpFlags' in translationDefinition:
576 for flag
in translationDefinition[
'calexpFlags']:
577 funcs[cls.
renameCol(flag, renameRules)] =
Column(flag, dataset=
'calexp')
579 if 'refFlags' in translationDefinition:
580 for flag
in translationDefinition[
'refFlags']:
583 if 'forcedFlags' in translationDefinition:
584 for flag
in translationDefinition[
'forcedFlags']:
585 funcs[cls.
renameCol(flag, renameRules)] =
Column(flag, dataset=
'forced_src')
587 if 'flags' in translationDefinition:
588 for flag
in translationDefinition[
'flags']:
591 return cls(funcs, **kwargs)
595 """Evaluate an expression on a DataFrame, knowing what the 'mag' function means
597 Builds on `pandas.DataFrame.eval`, which parses and executes math on dataframes.
601 df : pandas.DataFrame
602 Dataframe on which to evaluate expression.
608 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>)/log(10)', expr)
609 val = df.eval(expr_new)
610 except Exception
as e:
611 log.error(
"Exception in mag_aware_eval: %s: %s",
type(e).__name__, e)
612 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>_instFlux)/log(10)', expr)
613 val = df.eval(expr_new)
618 """Arbitrary computation on a catalog
620 Column names (and thus the columns to be loaded
from catalog) are found
621 by finding all words
and trying to ignore all
"math-y" words.
626 Expression to evaluate, to be parsed
and executed by `mag_aware_eval`.
628 _ignore_words = ('mag',
'sin',
'cos',
'exp',
'log',
'sqrt')
640 flux_cols = re.findall(
r'mag\(\s*(\w+)\s*\)', self.
expr)
642 cols = [c
for c
in re.findall(
r'[a-zA-Z_]+', self.
expr)
if c
not in self.
_ignore_words]
645 if not re.search(
'_instFlux$', c):
646 cols.append(f
'{c}_instFlux')
651 return list(
set([c
for c
in cols
if c
not in not_a_col]))
658 """Get column with specified name
678 """Return the value of the index for each object
681 columns = ['coord_ra']
682 _defaultDataset =
'ref'
686 return pd.Series(df.index, index=df.index)
691 _allow_difference =
False
695 return pd.Series(df.index, index=df.index)
699 col =
'base_Footprint_nPix'
703 """Base class for coordinate column, in degrees
712 output = df[self.
col] * 180 / np.pi
if self.
_radians else df[self.
col]
717 """Right Ascension, in degrees
723 super().
__init__(
'coord_ra', **kwargs)
726 return super().
__call__(catalog, **kwargs)
730 """Declination, in degrees
736 super().
__init__(
'coord_dec', **kwargs)
739 return super().
__call__(catalog, **kwargs)
743 """Compute the level 20 HtmIndex for the catalog.
747 This functor was implemented to satisfy requirements of old APDB interface
748 which required ``pixelId`` column in DiaObject
with HTM20 index. APDB
749 interface had migrated to
not need that information, but we keep this
750 class in case it may be useful for something else.
765 def computePixel(row):
774 return self.
pixelator.index(sphPoint.getVector())
776 return df.apply(computePixel, axis=1, result_type=
'reduce').astype(
'int64')
780 if not col.endswith(
'_instFlux'):
786 if not col.endswith(
'_instFluxErr'):
787 col +=
'_instFluxErr'
792 """Compute calibrated magnitude
794 Takes a `calib` argument, which returns the flux at mag=0
795 as `calib.getFluxMag0()`. If
not provided, then the default
796 `fluxMag0`
is 63095734448.0194, which
is default
for HSC.
797 This default should be removed
in DM-21955
799 This calculation hides warnings about invalid values
and dividing by zero.
801 As
for all functors, a `dataset`
and `filt` kwarg should be provided upon
802 initialization. Unlike the default `Functor`, however, the default dataset
803 for a `Mag`
is `
'meas'`, rather than `
'ref'`.
808 Name of flux column
from which to compute magnitude. Can be parseable
809 by `lsst.pipe.tasks.functors.fluxName` function---that
is, you can
pass
810 `
'modelfit_CModel'` instead of `
'modelfit_CModel_instFlux'`)
and it will
812 calib : `lsst.afw.image.calib.Calib` (optional)
813 Object that knows zero point.
815 _defaultDataset = 'meas'
820 if calib
is not None:
833 with np.warnings.catch_warnings():
834 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
835 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
840 return f
'mag_{self.col}'
844 """Compute calibrated magnitude uncertainty
851 calib : `lsst.afw.image.calib.Calib` (optional)
852 Object that knows zero point.
857 if self.
calib is not None:
864 return [self.
col, self.
col +
'Err']
867 with np.warnings.catch_warnings():
868 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
869 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
871 x = df[fluxErrCol] / df[fluxCol]
873 magErr = (2.5 / np.log(10.)) * np.sqrt(x*x + y*y)
878 return super().name +
'_err'
890 _defaultDataset =
'meas'
892 """Functor to calculate magnitude difference"""
904 with np.warnings.catch_warnings():
905 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
906 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
907 return -2.5*np.log10(df[self.
col1]/df[self.
col2])
911 return f
'(mag_{self.col1} - mag_{self.col2})'
915 return f
'magDiff_{self.col1}_{self.col2}'
919 """Compute the color between two filters
921 Computes color by initializing two different `Mag`
922 functors based on the `col` and filters provided,
and
923 then returning the difference.
925 This
is enabled by the `_func` expecting a dataframe
with a
926 multilevel column index,
with both `
'band'`
and `
'column'`,
927 instead of just `
'column'`, which
is the `Functor` default.
928 This
is controlled by the `_dfLevels` attribute.
930 Also of note, the default dataset
for `Color`
is `forced_src
'`,
931 whereas for `Mag` it
is `
'meas'`.
936 Name of flux column
from which to compute; same
as would be passed to
940 Filters
from which to compute magnitude difference.
941 Color computed
is `
Mag(filt2) -
Mag(filt1)`.
943 _defaultDataset = 'forced_src'
944 _dfLevels = (
'band',
'column')
950 raise RuntimeError(
"Cannot compute Color for %s: %s - %s " % (col, filt2, filt1))
968 mag2 = self.mag2._func(df[self.filt2])
969 mag1 = self.mag1._func(df[self.filt1])
974 return [self.
mag1.col, self.
mag2.col]
981 return f
'{self.filt2} - {self.filt1} ({self.col})'
985 return f
"{self.col}_{self.filt2.replace('-', '')}m{self.filt1.replace('-', '')}"
989 """Main function of this subclass is to override the dropna=True
992 _allow_difference =
False
997 return super().
__call__(parq, dropna=
False, **kwargs)
1001 _columns = [
"base_ClassificationExtendedness_value"]
1002 _column =
"base_ClassificationExtendedness_value"
1004 def _func(self, df):
1007 test = (x < 0.5).astype(int)
1008 test = test.mask(mask, 2)
1013 label = pd.Series(pd.Categorical.from_codes(test, categories=categories),
1014 index=x.index, name=
'label')
1016 label = label.astype(str)
1021 _columns = [
'numStarFlags']
1022 labels = {
"star": 0,
"maybe": 1,
"notStar": 2}
1024 def _func(self, df):
1028 n = len(x.unique()) - 1
1030 labels = [
'noStar',
'maybe',
'star']
1031 label = pd.Series(pd.cut(x, [-1, 0, n-1, n], labels=labels),
1032 index=x.index, name=
'label')
1035 label = label.astype(str)
1041 name =
'Deconvolved Moments'
1042 shortname =
'deconvolvedMoments'
1043 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1044 "ext_shapeHSM_HsmSourceMoments_yy",
1045 "base_SdssShape_xx",
"base_SdssShape_yy",
1046 "ext_shapeHSM_HsmPsfMoments_xx",
1047 "ext_shapeHSM_HsmPsfMoments_yy")
1049 def _func(self, df):
1050 """Calculate deconvolved moments"""
1051 if "ext_shapeHSM_HsmSourceMoments_xx" in df.columns:
1052 hsm = df[
"ext_shapeHSM_HsmSourceMoments_xx"] + df[
"ext_shapeHSM_HsmSourceMoments_yy"]
1054 hsm = np.ones(len(df))*np.nan
1055 sdss = df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]
1056 if "ext_shapeHSM_HsmPsfMoments_xx" in df.columns:
1057 psf = df[
"ext_shapeHSM_HsmPsfMoments_xx"] + df[
"ext_shapeHSM_HsmPsfMoments_yy"]
1062 raise RuntimeError(
'No psf shape parameter found in catalog')
1064 return hsm.where(np.isfinite(hsm), sdss) - psf
1068 """Functor to calculate SDSS trace radius size for sources"""
1069 name =
"SDSS Trace Size"
1070 shortname =
'sdssTrace'
1071 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy")
1073 def _func(self, df):
1074 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1079 """Functor to calculate SDSS trace radius size difference (%) between object and psf model"""
1080 name =
"PSF - SDSS Trace Size"
1081 shortname =
'psf_sdssTrace'
1082 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy",
1083 "base_SdssShape_psf_xx",
"base_SdssShape_psf_yy")
1085 def _func(self, df):
1086 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1087 psfSize = np.sqrt(0.5*(df[
"base_SdssShape_psf_xx"] + df[
"base_SdssShape_psf_yy"]))
1088 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1093 """Functor to calculate HSM trace radius size for sources"""
1094 name =
'HSM Trace Size'
1095 shortname =
'hsmTrace'
1096 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1097 "ext_shapeHSM_HsmSourceMoments_yy")
1099 def _func(self, df):
1100 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1101 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1106 """Functor to calculate HSM trace radius size difference (%) between object and psf model"""
1107 name =
'PSF - HSM Trace Size'
1108 shortname =
'psf_HsmTrace'
1109 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1110 "ext_shapeHSM_HsmSourceMoments_yy",
1111 "ext_shapeHSM_HsmPsfMoments_xx",
1112 "ext_shapeHSM_HsmPsfMoments_yy")
1114 def _func(self, df):
1115 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1116 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1117 psfSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmPsfMoments_xx"]
1118 + df[
"ext_shapeHSM_HsmPsfMoments_yy"]))
1119 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1124 name =
'HSM Psf FWHM'
1125 _columns = (
'ext_shapeHSM_HsmPsfMoments_xx',
'ext_shapeHSM_HsmPsfMoments_yy')
1128 SIGMA2FWHM = 2*np.sqrt(2*np.log(2))
1130 def _func(self, df):
1132 0.5*(df[
'ext_shapeHSM_HsmPsfMoments_xx'] + df[
'ext_shapeHSM_HsmPsfMoments_yy']))
1136 name =
"Distortion Ellipticity (e1)"
1137 shortname =
"Distortion"
1150 def _func(self, df):
1155 name =
"Ellipticity e2"
1167 def _func(self, df):
1183 def _func(self, df):
1188 """Computations using the stored localWcs.
1190 name = "LocalWcsOperations"
1205 """Compute the distance on the sphere from x2, y1 to x1, y1.
1213 cd11 : `pandas.Series`
1214 [1, 1] element of the local Wcs affine transform.
1215 cd11 : `pandas.Series`
1216 [1, 1] element of the local Wcs affine transform.
1217 cd12 : `pandas.Series`
1218 [1, 2] element of the local Wcs affine transform.
1219 cd21 : `pandas.Series`
1220 [2, 1] element of the local Wcs affine transform.
1221 cd22 : `pandas.Series`
1222 [2, 2] element of the local Wcs affine transform.
1227 RA and dec conversion of x
and y given the local Wcs. Returned
1228 units are
in radians.
1231 return (x * cd11 + y * cd12, x * cd21 + y * cd22)
1234 """Compute the local pixel scale conversion.
1238 ra1 : `pandas.Series`
1239 Ra of the first coordinate in radians.
1240 dec1 : `pandas.Series`
1241 Dec of the first coordinate
in radians.
1242 ra2 : `pandas.Series`
1243 Ra of the second coordinate
in radians.
1244 dec2 : `pandas.Series`
1245 Dec of the second coordinate
in radians.
1249 dist : `pandas.Series`
1250 Distance on the sphere
in radians.
1252 deltaDec = dec2 - dec1
1254 return 2 * np.arcsin(
1256 np.sin(deltaDec / 2) ** 2
1257 + np.cos(dec2) * np.cos(dec1) * np.sin(deltaRa / 2) ** 2))
1260 """Compute the distance on the sphere from x2, y1 to x1, y1.
1264 x1 : `pandas.Series`
1266 y1 : `pandas.Series`
1268 x2 : `pandas.Series`
1270 y2 : `pandas.Series`
1272 cd11 : `pandas.Series`
1273 [1, 1] element of the local Wcs affine transform.
1274 cd11 : `pandas.Series`
1275 [1, 1] element of the local Wcs affine transform.
1276 cd12 : `pandas.Series`
1277 [1, 2] element of the local Wcs affine transform.
1278 cd21 : `pandas.Series`
1279 [2, 1] element of the local Wcs affine transform.
1280 cd22 : `pandas.Series`
1281 [2, 2] element of the local Wcs affine transform.
1285 Distance : `pandas.Series`
1286 Arcseconds per pixel at the location of the local WC
1295 """Compute the local pixel scale from the stored CDMatrix.
1307 """Compute the local pixel to scale conversion in arcseconds.
1311 cd11 : `pandas.Series`
1312 [1, 1] element of the local Wcs affine transform in radians.
1313 cd11 : `pandas.Series`
1314 [1, 1] element of the local Wcs affine transform
in radians.
1315 cd12 : `pandas.Series`
1316 [1, 2] element of the local Wcs affine transform
in radians.
1317 cd21 : `pandas.Series`
1318 [2, 1] element of the local Wcs affine transform
in radians.
1319 cd22 : `pandas.Series`
1320 [2, 2] element of the local Wcs affine transform
in radians.
1324 pixScale : `pandas.Series`
1325 Arcseconds per pixel at the location of the local WC
1327 return 3600 * np.degrees(np.sqrt(np.fabs(cd11 * cd22 - cd12 * cd21)))
1329 def _func(self, df):
1337 """Convert a value in units pixels squared to units arcseconds squared.
1356 return f
"{self.col}_asArcseconds"
1366 def _func(self, df):
1374 """Convert a value in units pixels to units arcseconds.
1393 return f
"{self.col}_asArcsecondsSq"
1403 def _func(self, df):
1408 return df[self.
col] * pixScale * pixScale
1412 name =
'Reference Band'
1413 shortname =
'refBand'
1417 return [
"merge_measurement_i",
1418 "merge_measurement_r",
1419 "merge_measurement_z",
1420 "merge_measurement_y",
1421 "merge_measurement_g",
1422 "merge_measurement_u"]
1424 def _func(self, df: pd.DataFrame) -> pd.Series:
1425 def getFilterAliasName(row):
1427 colName = row.idxmax()
1428 return colName.replace(
'merge_measurement_',
'')
1432 columns = [col
for col
in self.
columnscolumns if col
in df.columns]
1434 return df[columns].apply(getFilterAliasName, axis=1,
1435 result_type=
'reduce').astype(
'object')
1440 AB_FLUX_SCALE = (0 * u.ABmag).to_value(u.nJy)
1441 LOG_AB_FLUX_SCALE = 12.56
1442 FIVE_OVER_2LOG10 = 1.085736204758129569
1446 def __init__(self, colFlux, colFluxErr=None, calib=None, **kwargs):
1452 if calib
is not None:
1466 return f
'mag_{self.col}'
1470 if np.abs(a) < np.abs(b):
1475 return np.abs(a) * np.sqrt(1. + q*q)
1481 with np.warnings.catch_warnings():
1482 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1483 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
1484 return -2.5 * np.log10(dn/fluxMag0)
1487 retVal = self.
vhypot(dn * fluxMag0Err, dnErr * fluxMag0)
1492 retVal = self.
dn2fluxErr(dn, dnErr, fluxMag0, fluxMag0Err) / self.
dn2flux(dn, fluxMag0)
1497 def _func(self, df):
1506 def _func(self, df):
1508 return pd.Series(retArr, index=df.index)
1512 def _func(self, df):
1521 def _func(self, df):
1523 return pd.Series(retArr, index=df.index)
1527 """Base class for calibrating the specified instrument flux column using
1528 the local photometric calibration.
1533 Name of the instrument flux column.
1534 instFluxErrCol : `str`
1535 Name of the assocated error columns for ``instFluxCol``.
1536 photoCalibCol : `str`
1537 Name of local calibration column.
1538 photoCalibErrCol : `str`
1539 Error associated
with ``photoCalibCol``
1549 logNJanskyToAB = (1 * u.nJy).to_value(u.ABmag)
1564 """Convert instrument flux to nanojanskys.
1568 instFlux : `numpy.ndarray` or `pandas.Series`
1569 Array of instrument flux measurements
1570 localCalib : `numpy.ndarray`
or `pandas.Series`
1571 Array of local photometric calibration estimates.
1575 calibFlux : `numpy.ndarray`
or `pandas.Series`
1576 Array of calibrated flux measurements.
1578 return instFlux * localCalib
1581 """Convert instrument flux to nanojanskys.
1585 instFlux : `numpy.ndarray` or `pandas.Series`
1586 Array of instrument flux measurements
1587 instFluxErr : `numpy.ndarray`
or `pandas.Series`
1588 Errors on associated ``instFlux`` values
1589 localCalib : `numpy.ndarray`
or `pandas.Series`
1590 Array of local photometric calibration estimates.
1591 localCalibErr : `numpy.ndarray`
or `pandas.Series`
1592 Errors on associated ``localCalib`` values
1596 calibFluxErr : `numpy.ndarray`
or `pandas.Series`
1597 Errors on calibrated flux measurements.
1599 return np.hypot(instFluxErr * localCalib, instFlux * localCalibErr)
1602 """Convert instrument flux to nanojanskys.
1606 instFlux : `numpy.ndarray` or `pandas.Series`
1607 Array of instrument flux measurements
1608 localCalib : `numpy.ndarray`
or `pandas.Series`
1609 Array of local photometric calibration estimates.
1613 calibMag : `numpy.ndarray`
or `pandas.Series`
1614 Array of calibrated AB magnitudes.
1619 """Convert instrument flux err to nanojanskys.
1623 instFlux : `numpy.ndarray` or `pandas.Series`
1624 Array of instrument flux measurements
1625 instFluxErr : `numpy.ndarray`
or `pandas.Series`
1626 Errors on associated ``instFlux`` values
1627 localCalib : `numpy.ndarray`
or `pandas.Series`
1628 Array of local photometric calibration estimates.
1629 localCalibErr : `numpy.ndarray`
or `pandas.Series`
1630 Errors on associated ``localCalib`` values
1634 calibMagErr: `numpy.ndarray`
or `pandas.Series`
1635 Error on calibrated AB magnitudes.
1642 """Compute calibrated fluxes using the local calibration value.
1658 return f
'flux_{self.instFluxCol}'
1660 def _func(self, df):
1665 """Compute calibrated flux errors using the local calibration value.
1682 return f
'fluxErr_{self.instFluxCol}'
1684 def _func(self, df):
1690 """Compute calibrated AB magnitudes using the local calibration value.
1706 return f
'mag_{self.instFluxCol}'
1708 def _func(self, df):
1714 """Compute calibrated AB magnitude errors using the local calibration value.
1731 return f
'magErr_{self.instFluxCol}'
1733 def _func(self, df):
1741 """Compute absolute mean of dipole fluxes.
1750 LocalDipoleMeanFluxErr
1752 LocalDipoleDiffFluxErr
1782 return f
'dipMeanFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1784 def _func(self, df):
1790 """Compute the error on the absolute mean of dipole fluxes.
1799 LocalDipoleMeanFluxErr
1801 LocalDipoleDiffFluxErr
1815 return f
'dipMeanFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1817 def _func(self, df):
1826 """Compute the absolute difference of dipole fluxes.
1828 Value is (abs(pos) - abs(neg))
1837 LocalDipoleMeanFluxErr
1839 LocalDipoleDiffFluxErr
1850 return f
'dipDiffFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1852 def _func(self, df):
1858 """Compute the error on the absolute difference of dipole fluxes.
1867 LocalDipoleMeanFluxErr
1869 LocalDipoleDiffFluxErr
1883 return f
'dipDiffFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1885 def _func(self, df):
1894 """Base class for returning the ratio of 2 columns.
1896 Can be used to compute a Signal to Noise ratio for any input flux.
1901 Name of the column to use at the numerator
in the ratio
1903 Name of the column to use
as the denominator
in the ratio.
1919 return f
'ratio_{self.numerator}_{self.denominator}'
1921 def _func(self, df):
1922 with np.warnings.catch_warnings():
1923 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1924 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
1929 """Compute E(B-V) from dustmaps.sfd
1931 _defaultDataset = 'ref'
1936 self.
_columns = [
'coord_ra',
'coord_dec']
1940 def _func(self, df):
1941 coords = SkyCoord(df[
'coord_ra']*u.rad, df[
'coord_dec']*u.rad)
1942 ebv = self.
sfd(coords)
1945 return pd.Series(ebv, index=df.index).astype(
'float64')
table::Key< std::string > name
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, log)
def init_fromDict(initDict, basePath='lsst.pipe.tasks.functors', typeKey='functor', name=None)