22__all__ = [
"Functor",
"CompositeFunctor",
"CustomFunctor",
"Column",
"Index",
23 "CoordColumn",
"RAColumn",
"DecColumn",
"HtmIndex20",
"Mag",
24 "MagErr",
"MagDiff",
"Color",
"DeconvolvedMoments",
"SdssTraceSize",
25 "PsfSdssTraceSizeDiff",
"HsmTraceSize",
"PsfHsmTraceSizeDiff",
26 "HsmFwhm",
"E1",
"E2",
"RadiusFromQuadrupole",
"LocalWcs",
27 "ComputePixelScale",
"ConvertPixelToArcseconds",
28 "ConvertPixelSqToArcsecondsSq",
"ReferenceBand",
"Photometry",
29 "NanoJansky",
"NanoJanskyErr",
"LocalPhotometry",
"LocalNanojansky",
30 "LocalNanojanskyErr",
"LocalDipoleMeanFlux",
31 "LocalDipoleMeanFluxErr",
"LocalDipoleDiffFlux",
32 "LocalDipoleDiffFluxErr",
"Ebv",
37from itertools
import product
43import astropy.units
as u
44from astropy.coordinates
import SkyCoord
46from lsst.utils
import doImport
47from lsst.utils.introspection
import get_full_type_name
48from lsst.daf.butler
import DeferredDatasetHandle
55 typeKey='functor', name=None):
56 """Initialize an object defined in a dictionary
58 The object needs to be importable as
59 f
'{basePath}.{initDict[typeKey]}'
60 The positional
and keyword arguments (
if any) are contained
in
61 "args" and "kwargs" entries
in the dictionary, respectively.
62 This
is used
in `functors.CompositeFunctor.from_yaml` to initialize
63 a composite functor
from a specification
in a YAML file.
68 Dictionary describing object
's initialization. Must contain
69 an entry keyed by ``typeKey`` that is the name of the object,
70 relative to ``basePath``.
72 Path relative to module
in which ``initDict[typeKey]``
is defined.
74 Key of ``initDict`` that
is the name of the object
75 (relative to `basePath`).
77 initDict = initDict.copy()
79 pythonType = doImport(f
'{basePath}.{initDict.pop(typeKey)}')
81 if 'args' in initDict:
82 args = initDict.pop(
'args')
83 if isinstance(args, str):
86 element = pythonType(*args, **initDict)
87 except Exception
as e:
88 message = f
'Error in constructing functor "{name}" of type {pythonType.__name__} with args: {args}'
89 raise type(e)(message, e.args)
94 """Define and execute a calculation on a DataFrame or Handle holding a DataFrame.
96 The `__call__` method accepts either a `DataFrame` object or a
97 `DeferredDatasetHandle`
or `InMemoryDatasetHandle`,
and returns the
98 result of the calculation
as a single column. Each functor defines what
99 columns are needed
for the calculation,
and only these columns are read
100 from the dataset handle.
102 The action of `__call__` consists of two steps: first, loading the
103 necessary columns
from disk into memory
as a `pandas.DataFrame` object;
104 and second, performing the computation on this dataframe
and returning the
108 To define a new `Functor`, a subclass must define a `_func` method,
109 that takes a `pandas.DataFrame`
and returns result
in a `pandas.Series`.
110 In addition, it must define the following attributes
112 * `_columns`: The columns necessary to perform the calculation
113 * `name`: A name appropriate
for a figure axis label
114 * `shortname`: A name appropriate
for use
as a dictionary key
116 On initialization, a `Functor` should declare what band (`filt` kwarg)
117 and dataset (e.g. `
'ref'`, `
'meas'`, `
'forced_src'`) it
is intended to be
118 applied to. This enables the `_get_data` method to extract the proper
119 columns
from the underlying data. If
not specified, the dataset will fall back
120 on the `_defaultDataset`attribute. If band
is not specified
and `dataset`
121 is anything other than `
'ref'`, then an error will be raised when trying to
122 perform the calculation.
124 Originally, `Functor` was set up to expect
125 datasets formatted like the `deepCoadd_obj` dataset; that
is, a
126 dataframe
with a multi-level column index,
with the levels of the
127 column index being `band`, `dataset`,
and `column`.
128 It has since been generalized to apply to dataframes without mutli-level
129 indices
and multi-level indices
with just `dataset`
and `column` levels.
130 In addition, the `_get_data` method that reads
131 the columns
from the underlying data will
return a dataframe
with column
132 index levels defined by the `_dfLevels` attribute; by default, this
is
135 The `_dfLevels` attributes should generally
not need to
136 be changed, unless `_func` needs columns
from multiple filters
or datasets
137 to do the calculation.
139 which `_dfLevels = (
'band',
'column')`,
and `_func` expects the dataframe
140 it gets to have those levels
in the column index.
145 Filter upon which to do the calculation
148 Dataset upon which to do the calculation
149 (e.g.,
'ref',
'meas',
'forced_src').
152 _defaultDataset = 'ref'
153 _dfLevels = (
'column',)
154 _defaultNoDup =
False
156 def __init__(self, filt=None, dataset=None, noDup=None):
160 self.
log = logging.getLogger(type(self).__name__)
164 if self.
_noDup is not None:
171 """Columns required to perform calculation
173 if not hasattr(self,
'_columns'):
174 raise NotImplementedError(
'Must define columns property or _columns attribute')
177 def _get_data_columnLevels(self, data, columnIndex=None):
178 """Gets the names of the column index levels
180 This should only be called in the context of a multilevel table.
185 The data to be read, can be a `DeferredDatasetHandle`
or
186 `InMemoryDatasetHandle`.
187 columnnIndex (optional): pandas `Index` object
188 If
not passed, then it
is read
from the `DeferredDatasetHandle`
189 for `InMemoryDatasetHandle`.
191 if columnIndex
is None:
192 columnIndex = data.get(component=
"columns")
193 return columnIndex.names
195 def _get_data_columnLevelNames(self, data, columnIndex=None):
196 """Gets the content of each of the column levels for a multilevel table.
198 if columnIndex
is None:
199 columnIndex = data.get(component=
"columns")
201 columnLevels = columnIndex.names
203 level: list(np.unique(np.array([c
for c
in columnIndex])[:, i]))
204 for i, level
in enumerate(columnLevels)
206 return columnLevelNames
208 def _colsFromDict(self, colDict, columnIndex=None):
209 """Converts dictionary column specficiation to a list of columns
214 for i, lev
in enumerate(columnLevels):
216 if isinstance(colDict[lev], str):
217 new_colDict[lev] = [colDict[lev]]
219 new_colDict[lev] = colDict[lev]
221 new_colDict[lev] = columnIndex.levels[i]
223 levelCols = [new_colDict[lev]
for lev
in columnLevels]
224 cols = list(product(*levelCols))
225 colsAvailable = [col
for col
in cols
if col
in columnIndex]
229 """Returns columns needed by functor from multilevel dataset
231 To access tables with multilevel column structure, the `DeferredDatasetHandle`
232 or `InMemoryDatasetHandle` need to be passed either a list of tuples
or a
238 The data
as either `DeferredDatasetHandle`,
or `InMemoryDatasetHandle`.
239 columnIndex (optional): pandas `Index` object
240 either passed
or read
in from `DeferredDatasetHandle`.
241 `returnTuple` : `bool`
242 If true, then
return a list of tuples rather than the column dictionary
243 specification. This
is set to `
True` by `CompositeFunctor`
in order to be able to
244 combine columns
from the various component functors.
247 if not isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
248 raise RuntimeError(f
"Unexpected data type. Got {get_full_type_name(data)}.")
250 if columnIndex
is None:
251 columnIndex = data.get(component=
"columns")
256 columnDict = {
'column': self.
columns,
258 if self.
filt is None:
260 if "band" in columnLevels:
262 columnDict[
"band"] = columnLevelNames[
"band"][0]
264 raise ValueError(f
"'filt' not set for functor {self.name}"
265 f
"(dataset {self.dataset}) "
267 "contains multiple filters in column index. "
268 "Set 'filt' or set 'dataset' to 'ref'.")
270 columnDict[
'band'] = self.
filt
273 return self.
_colsFromDict(columnDict, columnIndex=columnIndex)
277 def _func(self, df, dropna=True):
278 raise NotImplementedError(
'Must define calculation on dataframe')
280 def _get_columnIndex(self, data):
281 """Return columnIndex
284 if isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
285 return data.get(component=
"columns")
289 def _get_data(self, data):
290 """Retrieve dataframe necessary for calculation.
292 The data argument can be a `DataFrame`, a `DeferredDatasetHandle`, or an
293 `InMemoryDatasetHandle`.
295 Returns dataframe upon which `self.
_func` can act.
299 if isinstance(data, pd.DataFrame):
300 _data = InMemoryDatasetHandle(data, storageClass=
"DataFrame")
301 elif isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
304 raise RuntimeError(f
"Unexpected type provided for data. Got {get_full_type_name(data)}.")
308 is_multiLevel = isinstance(columnIndex, pd.MultiIndex)
317 df = _data.get(parameters={
"columns": columns})
325 def _setLevels(self, df):
326 levelsToDrop = [n
for n
in df.columns.names
if n
not in self.
_dfLevels]
327 df.columns = df.columns.droplevel(levelsToDrop)
330 def _dropna(self, vals):
336 vals = self.
_func(df)
337 except Exception
as e:
338 self.
log.error(
"Exception in %s call: %s: %s", self.
name, type(e).__name__, e)
346 """Computes difference between functor called on two different DataFrame/Handle objects
348 return self(data1, **kwargs) - self(data2, **kwargs)
351 return pd.Series(np.full(len(df), np.nan), index=df.index)
355 """Full name of functor (suitable for figure labels)
357 return NotImplementedError
361 """Short name of functor (suitable for column name/dict key)
367 """Perform multiple calculations at once on a catalog.
369 The role of a `CompositeFunctor` is to group together computations
from
370 multiple functors. Instead of returning `pandas.Series` a
371 `CompositeFunctor` returns a `pandas.Dataframe`,
with the column names
372 being the keys of `funcDict`.
374 The `columns` attribute of a `CompositeFunctor`
is the union of all columns
375 in all the component functors.
377 A `CompositeFunctor` does
not use a `_func` method itself; rather,
378 when a `CompositeFunctor`
is called, all its columns are loaded
379 at once,
and the resulting dataframe
is passed to the `_func` method of each component
380 functor. This has the advantage of only doing I/O (reading
from parquet file) once,
381 and works because each individual `_func` method of each component functor does
not
382 care
if there are *extra* columns
in the dataframe being passed; only that it must contain
383 *at least* the `columns` it expects.
385 An important
and useful
class method is `from_yaml`, which takes
as argument the path to a YAML
386 file specifying a collection of functors.
390 funcs : `dict`
or `list`
391 Dictionary
or list of functors. If a list, then it will be converted
392 into a dictonary according to the `.shortname` attribute of each functor.
396 name =
"CompositeFunctor"
400 if type(funcs) == dict:
403 self.
funcDict = {f.shortname: f
for f
in funcs}
421 if isinstance(new, dict):
423 elif isinstance(new, CompositeFunctor):
426 raise TypeError(
'Can only update with dictionary or CompositeFunctor.')
434 return list(set([x
for y
in [f.columns
for f
in self.
funcDict.values()]
for x
in y]))
443 f.multilevelColumns(data, returnTuple=
True, **kwargs)
for f
in self.
funcDict.values()
451 """Apply the functor to the data table
456 The data represented as `lsst.daf.butler.DeferredDatasetHandle`,
457 `lsst.pipe.base.InMemoryDatasetHandle`,
458 or `pandas.DataFrame`.
459 The table
or a pointer to a table on disk
from which columns can
462 if isinstance(data, pd.DataFrame):
463 _data = InMemoryDatasetHandle(data, storageClass=
"DataFrame")
464 elif isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
467 raise RuntimeError(f
"Unexpected type provided for data. Got {get_full_type_name(data)}.")
471 if isinstance(columnIndex, pd.MultiIndex):
473 df = _data.get(parameters={
"columns": columns})
478 subdf = f._setLevels(
479 df[f.multilevelColumns(_data, returnTuple=
True, columnIndex=columnIndex)]
481 valDict[k] = f._func(subdf)
482 except Exception
as e:
484 "Exception in %s (funcs: %s) call: %s",
490 valDict[k] = f.fail(subdf)
497 valDict = {k: f._func(df)
for k, f
in self.
funcDict.items()}
500 for name, colVal
in valDict.items():
501 if len(colVal.shape) != 1:
502 raise RuntimeError(
"Transformed column '%s' is not the shape of a column. "
503 "It is shaped %s and type %s." % (name, colVal.shape, type(colVal)))
506 valDf = pd.concat(valDict, axis=1)
508 print([(k, type(v))
for k, v
in valDict.items()])
511 if kwargs.get(
'dropna',
False):
512 valDf = valDf.dropna(how=
'any')
518 if renameRules
is None:
520 for old, new
in renameRules:
521 if col.startswith(old):
522 col = col.replace(old, new)
528 filename = os.path.expandvars(filename)
529 with open(filename)
as f:
530 translationDefinition = yaml.safe_load(f)
532 return cls.
from_yaml(translationDefinition, **kwargs)
537 for func, val
in translationDefinition[
'funcs'].items():
540 if 'flag_rename_rules' in translationDefinition:
541 renameRules = translationDefinition[
'flag_rename_rules']
545 if 'calexpFlags' in translationDefinition:
546 for flag
in translationDefinition[
'calexpFlags']:
547 funcs[cls.
renameCol(flag, renameRules)] =
Column(flag, dataset=
'calexp')
549 if 'refFlags' in translationDefinition:
550 for flag
in translationDefinition[
'refFlags']:
553 if 'forcedFlags' in translationDefinition:
554 for flag
in translationDefinition[
'forcedFlags']:
555 funcs[cls.
renameCol(flag, renameRules)] =
Column(flag, dataset=
'forced_src')
557 if 'flags' in translationDefinition:
558 for flag
in translationDefinition[
'flags']:
561 return cls(funcs, **kwargs)
565 """Evaluate an expression on a DataFrame, knowing what the 'mag' function means
567 Builds on `pandas.DataFrame.eval`, which parses and executes math on dataframes.
571 df : pandas.DataFrame
572 Dataframe on which to evaluate expression.
578 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>)/log(10)', expr)
579 val = df.eval(expr_new)
580 except Exception
as e:
581 log.error(
"Exception in mag_aware_eval: %s: %s", type(e).__name__, e)
582 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>_instFlux)/log(10)', expr)
583 val = df.eval(expr_new)
588 """Arbitrary computation on a catalog
590 Column names (and thus the columns to be loaded
from catalog) are found
591 by finding all words
and trying to ignore all
"math-y" words.
596 Expression to evaluate, to be parsed
and executed by `mag_aware_eval`.
598 _ignore_words = ('mag',
'sin',
'cos',
'exp',
'log',
'sqrt')
610 flux_cols = re.findall(
r'mag\(\s*(\w+)\s*\)', self.
expr)
612 cols = [c
for c
in re.findall(
r'[a-zA-Z_]+', self.
expr)
if c
not in self.
_ignore_words]
615 if not re.search(
'_instFlux$', c):
616 cols.append(f
'{c}_instFlux')
621 return list(set([c
for c
in cols
if c
not in not_a_col]))
628 """Get column with specified name
648 """Return the value of the index for each object
651 columns = ['coord_ra']
652 _defaultDataset =
'ref'
656 return pd.Series(df.index, index=df.index)
660 """Base class for coordinate column, in degrees
669 output = df[self.
col] * 180 / np.pi
if self.
_radians else df[self.
col]
674 """Right Ascension, in degrees
680 super().
__init__(
'coord_ra', **kwargs)
683 return super().
__call__(catalog, **kwargs)
687 """Declination, in degrees
693 super().
__init__(
'coord_dec', **kwargs)
696 return super().
__call__(catalog, **kwargs)
700 """Compute the level 20 HtmIndex for the catalog.
704 This functor was implemented to satisfy requirements of old APDB interface
705 which required ``pixelId`` column in DiaObject
with HTM20 index. APDB
706 interface had migrated to
not need that information, but we keep this
707 class in case it may be useful for something else.
722 def computePixel(row):
731 return self.
pixelator.index(sphPoint.getVector())
733 return df.apply(computePixel, axis=1, result_type=
'reduce').astype(
'int64')
737 if not col.endswith(
'_instFlux'):
743 if not col.endswith(
'_instFluxErr'):
744 col +=
'_instFluxErr'
749 """Compute calibrated magnitude
751 Takes a `calib` argument, which returns the flux at mag=0
752 as `calib.getFluxMag0()`. If
not provided, then the default
753 `fluxMag0`
is 63095734448.0194, which
is default
for HSC.
754 This default should be removed
in DM-21955
756 This calculation hides warnings about invalid values
and dividing by zero.
758 As
for all functors, a `dataset`
and `filt` kwarg should be provided upon
759 initialization. Unlike the default `Functor`, however, the default dataset
760 for a `Mag`
is `
'meas'`, rather than `
'ref'`.
765 Name of flux column
from which to compute magnitude. Can be parseable
766 by `lsst.pipe.tasks.functors.fluxName` function---that
is, you can
pass
767 `
'modelfit_CModel'` instead of `
'modelfit_CModel_instFlux'`)
and it will
769 calib : `lsst.afw.image.calib.Calib` (optional)
770 Object that knows zero point.
772 _defaultDataset = 'meas'
777 if calib
is not None:
790 with np.warnings.catch_warnings():
791 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
792 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
797 return f
'mag_{self.col}'
801 """Compute calibrated magnitude uncertainty
808 calib : `lsst.afw.image.calib.Calib` (optional)
809 Object that knows zero point.
814 if self.
calib is not None:
821 return [self.
col, self.
col +
'Err']
824 with np.warnings.catch_warnings():
825 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
826 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
828 x = df[fluxErrCol] / df[fluxCol]
830 magErr = (2.5 / np.log(10.)) * np.sqrt(x*x + y*y)
835 return super().name +
'_err'
839 _defaultDataset =
'meas'
841 """Functor to calculate magnitude difference"""
853 with np.warnings.catch_warnings():
854 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
855 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
856 return -2.5*np.log10(df[self.
col1]/df[self.
col2])
860 return f
'(mag_{self.col1} - mag_{self.col2})'
864 return f
'magDiff_{self.col1}_{self.col2}'
868 """Compute the color between two filters
870 Computes color by initializing two different `Mag`
871 functors based on the `col` and filters provided,
and
872 then returning the difference.
874 This
is enabled by the `_func` expecting a dataframe
with a
875 multilevel column index,
with both `
'band'`
and `
'column'`,
876 instead of just `
'column'`, which
is the `Functor` default.
877 This
is controlled by the `_dfLevels` attribute.
879 Also of note, the default dataset
for `Color`
is `forced_src
'`,
880 whereas for `Mag` it
is `
'meas'`.
885 Name of flux column
from which to compute; same
as would be passed to
889 Filters
from which to compute magnitude difference.
890 Color computed
is `
Mag(filt2) -
Mag(filt1)`.
892 _defaultDataset = 'forced_src'
893 _dfLevels = (
'band',
'column')
899 raise RuntimeError(
"Cannot compute Color for %s: %s - %s " % (col, filt2, filt1))
917 mag2 = self.mag2._func(df[self.filt2])
918 mag1 = self.mag1._func(df[self.filt1])
923 return [self.
mag1.col, self.
mag2.col]
930 return f
'{self.filt2} - {self.filt1} ({self.col})'
934 return f
"{self.col}_{self.filt2.replace('-', '')}m{self.filt1.replace('-', '')}"
938 name =
'Deconvolved Moments'
939 shortname =
'deconvolvedMoments'
940 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
941 "ext_shapeHSM_HsmSourceMoments_yy",
942 "base_SdssShape_xx",
"base_SdssShape_yy",
943 "ext_shapeHSM_HsmPsfMoments_xx",
944 "ext_shapeHSM_HsmPsfMoments_yy")
947 """Calculate deconvolved moments"""
948 if "ext_shapeHSM_HsmSourceMoments_xx" in df.columns:
949 hsm = df[
"ext_shapeHSM_HsmSourceMoments_xx"] + df[
"ext_shapeHSM_HsmSourceMoments_yy"]
951 hsm = np.ones(len(df))*np.nan
952 sdss = df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]
953 if "ext_shapeHSM_HsmPsfMoments_xx" in df.columns:
954 psf = df[
"ext_shapeHSM_HsmPsfMoments_xx"] + df[
"ext_shapeHSM_HsmPsfMoments_yy"]
959 raise RuntimeError(
'No psf shape parameter found in catalog')
961 return hsm.where(np.isfinite(hsm), sdss) - psf
965 """Functor to calculate SDSS trace radius size for sources"""
966 name =
"SDSS Trace Size"
967 shortname =
'sdssTrace'
968 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy")
971 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
976 """Functor to calculate SDSS trace radius size difference (%) between object and psf model"""
977 name =
"PSF - SDSS Trace Size"
978 shortname =
'psf_sdssTrace'
979 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy",
980 "base_SdssShape_psf_xx",
"base_SdssShape_psf_yy")
983 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
984 psfSize = np.sqrt(0.5*(df[
"base_SdssShape_psf_xx"] + df[
"base_SdssShape_psf_yy"]))
985 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
990 """Functor to calculate HSM trace radius size for sources"""
991 name =
'HSM Trace Size'
992 shortname =
'hsmTrace'
993 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
994 "ext_shapeHSM_HsmSourceMoments_yy")
997 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
998 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1003 """Functor to calculate HSM trace radius size difference (%) between object and psf model"""
1004 name =
'PSF - HSM Trace Size'
1005 shortname =
'psf_HsmTrace'
1006 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1007 "ext_shapeHSM_HsmSourceMoments_yy",
1008 "ext_shapeHSM_HsmPsfMoments_xx",
1009 "ext_shapeHSM_HsmPsfMoments_yy")
1011 def _func(self, df):
1012 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1013 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1014 psfSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmPsfMoments_xx"]
1015 + df[
"ext_shapeHSM_HsmPsfMoments_yy"]))
1016 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1021 name =
'HSM Psf FWHM'
1022 _columns = (
'ext_shapeHSM_HsmPsfMoments_xx',
'ext_shapeHSM_HsmPsfMoments_yy')
1025 SIGMA2FWHM = 2*np.sqrt(2*np.log(2))
1027 def _func(self, df):
1029 0.5*(df[
'ext_shapeHSM_HsmPsfMoments_xx'] + df[
'ext_shapeHSM_HsmPsfMoments_yy']))
1033 name =
"Distortion Ellipticity (e1)"
1034 shortname =
"Distortion"
1047 def _func(self, df):
1052 name =
"Ellipticity e2"
1064 def _func(self, df):
1080 def _func(self, df):
1085 """Computations using the stored localWcs.
1087 name = "LocalWcsOperations"
1102 """Compute the distance on the sphere from x2, y1 to x1, y1.
1110 cd11 : `pandas.Series`
1111 [1, 1] element of the local Wcs affine transform.
1112 cd11 : `pandas.Series`
1113 [1, 1] element of the local Wcs affine transform.
1114 cd12 : `pandas.Series`
1115 [1, 2] element of the local Wcs affine transform.
1116 cd21 : `pandas.Series`
1117 [2, 1] element of the local Wcs affine transform.
1118 cd22 : `pandas.Series`
1119 [2, 2] element of the local Wcs affine transform.
1124 RA and dec conversion of x
and y given the local Wcs. Returned
1125 units are
in radians.
1128 return (x * cd11 + y * cd12, x * cd21 + y * cd22)
1131 """Compute the local pixel scale conversion.
1135 ra1 : `pandas.Series`
1136 Ra of the first coordinate in radians.
1137 dec1 : `pandas.Series`
1138 Dec of the first coordinate
in radians.
1139 ra2 : `pandas.Series`
1140 Ra of the second coordinate
in radians.
1141 dec2 : `pandas.Series`
1142 Dec of the second coordinate
in radians.
1146 dist : `pandas.Series`
1147 Distance on the sphere
in radians.
1149 deltaDec = dec2 - dec1
1151 return 2 * np.arcsin(
1153 np.sin(deltaDec / 2) ** 2
1154 + np.cos(dec2) * np.cos(dec1) * np.sin(deltaRa / 2) ** 2))
1157 """Compute the distance on the sphere from x2, y1 to x1, y1.
1161 x1 : `pandas.Series`
1163 y1 : `pandas.Series`
1165 x2 : `pandas.Series`
1167 y2 : `pandas.Series`
1169 cd11 : `pandas.Series`
1170 [1, 1] element of the local Wcs affine transform.
1171 cd11 : `pandas.Series`
1172 [1, 1] element of the local Wcs affine transform.
1173 cd12 : `pandas.Series`
1174 [1, 2] element of the local Wcs affine transform.
1175 cd21 : `pandas.Series`
1176 [2, 1] element of the local Wcs affine transform.
1177 cd22 : `pandas.Series`
1178 [2, 2] element of the local Wcs affine transform.
1182 Distance : `pandas.Series`
1183 Arcseconds per pixel at the location of the local WC
1192 """Compute the local pixel scale from the stored CDMatrix.
1204 """Compute the local pixel to scale conversion in arcseconds.
1208 cd11 : `pandas.Series`
1209 [1, 1] element of the local Wcs affine transform in radians.
1210 cd11 : `pandas.Series`
1211 [1, 1] element of the local Wcs affine transform
in radians.
1212 cd12 : `pandas.Series`
1213 [1, 2] element of the local Wcs affine transform
in radians.
1214 cd21 : `pandas.Series`
1215 [2, 1] element of the local Wcs affine transform
in radians.
1216 cd22 : `pandas.Series`
1217 [2, 2] element of the local Wcs affine transform
in radians.
1221 pixScale : `pandas.Series`
1222 Arcseconds per pixel at the location of the local WC
1224 return 3600 * np.degrees(np.sqrt(np.fabs(cd11 * cd22 - cd12 * cd21)))
1226 def _func(self, df):
1234 """Convert a value in units pixels to units arcseconds.
1253 return f
"{self.col}_asArcseconds"
1263 def _func(self, df):
1271 """Convert a value in units pixels squared to units arcseconds squared.
1290 return f
"{self.col}_asArcsecondsSq"
1300 def _func(self, df):
1305 return df[self.
col] * pixScale * pixScale
1309 name =
'Reference Band'
1310 shortname =
'refBand'
1314 return [
"merge_measurement_i",
1315 "merge_measurement_r",
1316 "merge_measurement_z",
1317 "merge_measurement_y",
1318 "merge_measurement_g",
1319 "merge_measurement_u"]
1321 def _func(self, df: pd.DataFrame) -> pd.Series:
1322 def getFilterAliasName(row):
1324 colName = row.idxmax()
1325 return colName.replace(
'merge_measurement_',
'')
1329 columns = [col
for col
in self.
columnscolumns if col
in df.columns]
1331 return df[columns].apply(getFilterAliasName, axis=1,
1332 result_type=
'reduce').astype(
'object')
1337 AB_FLUX_SCALE = (0 * u.ABmag).to_value(u.nJy)
1338 LOG_AB_FLUX_SCALE = 12.56
1339 FIVE_OVER_2LOG10 = 1.085736204758129569
1343 def __init__(self, colFlux, colFluxErr=None, calib=None, **kwargs):
1349 if calib
is not None:
1363 return f
'mag_{self.col}'
1367 if np.abs(a) < np.abs(b):
1372 return np.abs(a) * np.sqrt(1. + q*q)
1378 with np.warnings.catch_warnings():
1379 np.warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1380 np.warnings.filterwarnings(
'ignore',
r'divide by zero')
1381 return -2.5 * np.log10(dn/fluxMag0)
1384 retVal = self.
vhypot(dn * fluxMag0Err, dnErr * fluxMag0)
1389 retVal = self.
dn2fluxErr(dn, dnErr, fluxMag0, fluxMag0Err) / self.
dn2flux(dn, fluxMag0)
1394 def _func(self, df):
1403 def _func(self, df):
1405 return pd.Series(retArr, index=df.index)
1409 """Base class for calibrating the specified instrument flux column using
1410 the local photometric calibration.
1415 Name of the instrument flux column.
1416 instFluxErrCol : `str`
1417 Name of the assocated error columns for ``instFluxCol``.
1418 photoCalibCol : `str`
1419 Name of local calibration column.
1420 photoCalibErrCol : `str`
1421 Error associated
with ``photoCalibCol``
1428 logNJanskyToAB = (1 * u.nJy).to_value(u.ABmag)
1443 """Convert instrument flux to nanojanskys.
1447 instFlux : `numpy.ndarray` or `pandas.Series`
1448 Array of instrument flux measurements
1449 localCalib : `numpy.ndarray`
or `pandas.Series`
1450 Array of local photometric calibration estimates.
1454 calibFlux : `numpy.ndarray`
or `pandas.Series`
1455 Array of calibrated flux measurements.
1457 return instFlux * localCalib
1460 """Convert instrument flux to nanojanskys.
1464 instFlux : `numpy.ndarray` or `pandas.Series`
1465 Array of instrument flux measurements
1466 instFluxErr : `numpy.ndarray`
or `pandas.Series`
1467 Errors on associated ``instFlux`` values
1468 localCalib : `numpy.ndarray`
or `pandas.Series`
1469 Array of local photometric calibration estimates.
1470 localCalibErr : `numpy.ndarray`
or `pandas.Series`
1471 Errors on associated ``localCalib`` values
1475 calibFluxErr : `numpy.ndarray`
or `pandas.Series`
1476 Errors on calibrated flux measurements.
1478 return np.hypot(instFluxErr * localCalib, instFlux * localCalibErr)
1481 """Convert instrument flux to nanojanskys.
1485 instFlux : `numpy.ndarray` or `pandas.Series`
1486 Array of instrument flux measurements
1487 localCalib : `numpy.ndarray`
or `pandas.Series`
1488 Array of local photometric calibration estimates.
1492 calibMag : `numpy.ndarray`
or `pandas.Series`
1493 Array of calibrated AB magnitudes.
1498 """Convert instrument flux err to nanojanskys.
1502 instFlux : `numpy.ndarray` or `pandas.Series`
1503 Array of instrument flux measurements
1504 instFluxErr : `numpy.ndarray`
or `pandas.Series`
1505 Errors on associated ``instFlux`` values
1506 localCalib : `numpy.ndarray`
or `pandas.Series`
1507 Array of local photometric calibration estimates.
1508 localCalibErr : `numpy.ndarray`
or `pandas.Series`
1509 Errors on associated ``localCalib`` values
1513 calibMagErr: `numpy.ndarray`
or `pandas.Series`
1514 Error on calibrated AB magnitudes.
1521 """Compute calibrated fluxes using the local calibration value."""
1529 return f
'flux_{self.instFluxCol}'
1531 def _func(self, df):
1536 """Compute calibrated flux errors using the local calibration value."""
1545 return f
'fluxErr_{self.instFluxCol}'
1547 def _func(self, df):
1553 """Compute absolute mean of dipole fluxes.
1559 LocalDipoleMeanFluxErr
1561 LocalDipoleDiffFluxErr
1591 return f
'dipMeanFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1593 def _func(self, df):
1599 """Compute the error on the absolute mean of dipole fluxes.
1607 LocalDipoleDiffFluxErr
1621 return f
'dipMeanFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1623 def _func(self, df):
1632 """Compute the absolute difference of dipole fluxes.
1634 Value is (abs(pos) - abs(neg))
1641 LocalDipoleMeanFluxErr
1642 LocalDipoleDiffFluxErr
1653 return f
'dipDiffFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1655 def _func(self, df):
1661 """Compute the error on the absolute difference of dipole fluxes.
1668 LocalDipoleMeanFluxErr
1683 return f
'dipDiffFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1685 def _func(self, df):
1694 """Compute E(B-V) from dustmaps.sfd
1696 _defaultDataset = 'ref'
1702 from dustmaps.sfd
import SFDQuery
1703 self.
_columns = [
'coord_ra',
'coord_dec']
1707 def _func(self, df):
1708 coords = SkyCoord(df[
'coord_ra'].values * u.rad, df[
'coord_dec'].values * u.rad)
1709 ebv = self.
sfd(coords)
1712 return pd.Series(ebv, index=df.index).astype(
'float64')
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, dec, **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 getSkySeparationFromPixel(self, x1, y1, x2, y2, cd11, cd12, cd21, cd22)
def computeSkySeparation(self, ra1, dec1, ra2, dec2)
def computeDeltaRaDec(self, x, y, 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 mag_aware_eval(df, expr, log)
def init_fromDict(initDict, basePath='lsst.pipe.tasks.functors', typeKey='functor', name=None)