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
44import astropy.units
as u
45from astropy.coordinates
import SkyCoord
47from lsst.utils
import doImport
48from lsst.utils.introspection
import get_full_type_name
49from lsst.daf.butler
import DeferredDatasetHandle
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 DataFrame or Handle holding a DataFrame.
97 The `__call__` method accepts either a `DataFrame` object or a
98 `DeferredDatasetHandle`
or `InMemoryDatasetHandle`,
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 dataset handle.
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 underlying data. 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 columns
from the underlying data 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').
153 _defaultDataset = 'ref'
154 _dfLevels = (
'column',)
155 _defaultNoDup =
False
157 def __init__(self, filt=None, dataset=None, noDup=None):
161 self.
log = logging.getLogger(type(self).__name__)
165 if self.
_noDup is not None:
172 """Columns required to perform calculation
174 if not hasattr(self,
'_columns'):
175 raise NotImplementedError(
'Must define columns property or _columns attribute')
178 def _get_data_columnLevels(self, data, columnIndex=None):
179 """Gets the names of the column index levels
181 This should only be called in the context of a multilevel table.
186 The data to be read, can be a `DeferredDatasetHandle`
or
187 `InMemoryDatasetHandle`.
188 columnnIndex (optional): pandas `Index` object
189 If
not passed, then it
is read
from the `DeferredDatasetHandle`
190 for `InMemoryDatasetHandle`.
192 if columnIndex
is None:
193 columnIndex = data.get(component=
"columns")
194 return columnIndex.names
196 def _get_data_columnLevelNames(self, data, columnIndex=None):
197 """Gets the content of each of the column levels for a multilevel table.
199 if columnIndex
is None:
200 columnIndex = data.get(component=
"columns")
202 columnLevels = columnIndex.names
204 level: list(np.unique(np.array([c
for c
in columnIndex])[:, i]))
205 for i, level
in enumerate(columnLevels)
207 return columnLevelNames
209 def _colsFromDict(self, colDict, columnIndex=None):
210 """Converts dictionary column specficiation to a list of columns
215 for i, lev
in enumerate(columnLevels):
217 if isinstance(colDict[lev], str):
218 new_colDict[lev] = [colDict[lev]]
220 new_colDict[lev] = colDict[lev]
222 new_colDict[lev] = columnIndex.levels[i]
224 levelCols = [new_colDict[lev]
for lev
in columnLevels]
225 cols = list(product(*levelCols))
226 colsAvailable = [col
for col
in cols
if col
in columnIndex]
230 """Returns columns needed by functor from multilevel dataset
232 To access tables with multilevel column structure, the `DeferredDatasetHandle`
233 or `InMemoryDatasetHandle` need to be passed either a list of tuples
or a
239 The data
as either `DeferredDatasetHandle`,
or `InMemoryDatasetHandle`.
240 columnIndex (optional): pandas `Index` object
241 either passed
or read
in from `DeferredDatasetHandle`.
242 `returnTuple` : `bool`
243 If true, then
return a list of tuples rather than the column dictionary
244 specification. This
is set to `
True` by `CompositeFunctor`
in order to be able to
245 combine columns
from the various component functors.
248 if not isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
249 raise RuntimeError(f
"Unexpected data type. Got {get_full_type_name(data)}.")
251 if columnIndex
is None:
252 columnIndex = data.get(component=
"columns")
257 columnDict = {
'column': self.
columns,
259 if self.
filt is None:
261 if "band" in columnLevels:
263 columnDict[
"band"] = columnLevelNames[
"band"][0]
265 raise ValueError(f
"'filt' not set for functor {self.name}"
266 f
"(dataset {self.dataset}) "
268 "contains multiple filters in column index. "
269 "Set 'filt' or set 'dataset' to 'ref'.")
271 columnDict[
'band'] = self.
filt
274 return self.
_colsFromDict(columnDict, columnIndex=columnIndex)
278 def _func(self, df, dropna=True):
279 raise NotImplementedError(
'Must define calculation on dataframe')
281 def _get_columnIndex(self, data):
282 """Return columnIndex
285 if isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
286 return data.get(component=
"columns")
290 def _get_data(self, data):
291 """Retrieve dataframe necessary for calculation.
293 The data argument can be a `DataFrame`, a `DeferredDatasetHandle`, or an
294 `InMemoryDatasetHandle`.
296 Returns dataframe upon which `self.
_func` can act.
300 if isinstance(data, pd.DataFrame):
301 _data = InMemoryDatasetHandle(data, storageClass=
"DataFrame")
302 elif isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
305 raise RuntimeError(f
"Unexpected type provided for data. Got {get_full_type_name(data)}.")
309 is_multiLevel = isinstance(columnIndex, pd.MultiIndex)
318 df = _data.get(parameters={
"columns": columns})
326 def _setLevels(self, df):
327 levelsToDrop = [n
for n
in df.columns.names
if n
not in self.
_dfLevels]
328 df.columns = df.columns.droplevel(levelsToDrop)
331 def _dropna(self, vals):
337 vals = self.
_func(df)
338 except Exception
as e:
339 self.
log.error(
"Exception in %s call: %s: %s", self.
name, type(e).__name__, e)
347 """Computes difference between functor called on two different DataFrame/Handle objects
349 return self(data1, **kwargs) - self(data2, **kwargs)
352 return pd.Series(np.full(len(df), np.nan), index=df.index)
356 """Full name of functor (suitable for figure labels)
358 return NotImplementedError
362 """Short name of functor (suitable for column name/dict key)
368 """Perform multiple calculations at once on a catalog.
370 The role of a `CompositeFunctor` is to group together computations
from
371 multiple functors. Instead of returning `pandas.Series` a
372 `CompositeFunctor` returns a `pandas.Dataframe`,
with the column names
373 being the keys of `funcDict`.
375 The `columns` attribute of a `CompositeFunctor`
is the union of all columns
376 in all the component functors.
378 A `CompositeFunctor` does
not use a `_func` method itself; rather,
379 when a `CompositeFunctor`
is called, all its columns are loaded
380 at once,
and the resulting dataframe
is passed to the `_func` method of each component
381 functor. This has the advantage of only doing I/O (reading
from parquet file) once,
382 and works because each individual `_func` method of each component functor does
not
383 care
if there are *extra* columns
in the dataframe being passed; only that it must contain
384 *at least* the `columns` it expects.
386 An important
and useful
class method is `from_yaml`, which takes
as argument the path to a YAML
387 file specifying a collection of functors.
391 funcs : `dict`
or `list`
392 Dictionary
or list of functors. If a list, then it will be converted
393 into a dictonary according to the `.shortname` attribute of each functor.
397 name =
"CompositeFunctor"
401 if type(funcs) == dict:
404 self.
funcDict = {f.shortname: f
for f
in funcs}
422 if isinstance(new, dict):
424 elif isinstance(new, CompositeFunctor):
427 raise TypeError(
'Can only update with dictionary or CompositeFunctor.')
435 return list(set([x
for y
in [f.columns
for f
in self.
funcDict.values()]
for x
in y]))
444 f.multilevelColumns(data, returnTuple=
True, **kwargs)
for f
in self.
funcDict.values()
452 """Apply the functor to the data table
457 The data represented as `lsst.daf.butler.DeferredDatasetHandle`,
458 `lsst.pipe.base.InMemoryDatasetHandle`,
459 or `pandas.DataFrame`.
460 The table
or a pointer to a table on disk
from which columns can
463 if isinstance(data, pd.DataFrame):
464 _data = InMemoryDatasetHandle(data, storageClass=
"DataFrame")
465 elif isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
468 raise RuntimeError(f
"Unexpected type provided for data. Got {get_full_type_name(data)}.")
472 if isinstance(columnIndex, pd.MultiIndex):
474 df = _data.get(parameters={
"columns": columns})
479 subdf = f._setLevels(
480 df[f.multilevelColumns(_data, returnTuple=
True, columnIndex=columnIndex)]
482 valDict[k] = f._func(subdf)
483 except Exception
as e:
485 "Exception in %s (funcs: %s) call: %s",
491 valDict[k] = f.fail(subdf)
498 valDict = {k: f._func(df)
for k, f
in self.
funcDict.items()}
501 for name, colVal
in valDict.items():
502 if len(colVal.shape) != 1:
503 raise RuntimeError(
"Transformed column '%s' is not the shape of a column. "
504 "It is shaped %s and type %s." % (name, colVal.shape, type(colVal)))
507 valDf = pd.concat(valDict, axis=1)
509 print([(k, type(v))
for k, v
in valDict.items()])
512 if kwargs.get(
'dropna',
False):
513 valDf = valDf.dropna(how=
'any')
519 if renameRules
is None:
521 for old, new
in renameRules:
522 if col.startswith(old):
523 col = col.replace(old, new)
529 filename = os.path.expandvars(filename)
530 with open(filename)
as f:
531 translationDefinition = yaml.safe_load(f)
533 return cls.
from_yaml(translationDefinition, **kwargs)
538 for func, val
in translationDefinition[
'funcs'].items():
541 if 'flag_rename_rules' in translationDefinition:
542 renameRules = translationDefinition[
'flag_rename_rules']
546 if 'calexpFlags' in translationDefinition:
547 for flag
in translationDefinition[
'calexpFlags']:
548 funcs[cls.
renameCol(flag, renameRules)] =
Column(flag, dataset=
'calexp')
550 if 'refFlags' in translationDefinition:
551 for flag
in translationDefinition[
'refFlags']:
554 if 'forcedFlags' in translationDefinition:
555 for flag
in translationDefinition[
'forcedFlags']:
556 funcs[cls.
renameCol(flag, renameRules)] =
Column(flag, dataset=
'forced_src')
558 if 'flags' in translationDefinition:
559 for flag
in translationDefinition[
'flags']:
562 return cls(funcs, **kwargs)
566 """Evaluate an expression on a DataFrame, knowing what the 'mag' function means
568 Builds on `pandas.DataFrame.eval`, which parses and executes math on dataframes.
572 df : pandas.DataFrame
573 Dataframe on which to evaluate expression.
579 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>)/log(10)', expr)
580 val = df.eval(expr_new)
581 except Exception
as e:
582 log.error(
"Exception in mag_aware_eval: %s: %s", type(e).__name__, e)
583 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>_instFlux)/log(10)', expr)
584 val = df.eval(expr_new)
589 """Arbitrary computation on a catalog
591 Column names (and thus the columns to be loaded
from catalog) are found
592 by finding all words
and trying to ignore all
"math-y" words.
597 Expression to evaluate, to be parsed
and executed by `mag_aware_eval`.
599 _ignore_words = ('mag',
'sin',
'cos',
'exp',
'log',
'sqrt')
611 flux_cols = re.findall(
r'mag\(\s*(\w+)\s*\)', self.
expr)
613 cols = [c
for c
in re.findall(
r'[a-zA-Z_]+', self.
expr)
if c
not in self.
_ignore_words]
616 if not re.search(
'_instFlux$', c):
617 cols.append(f
'{c}_instFlux')
622 return list(set([c
for c
in cols
if c
not in not_a_col]))
629 """Get column with specified name
649 """Return the value of the index for each object
652 columns = ['coord_ra']
653 _defaultDataset =
'ref'
657 return pd.Series(df.index, index=df.index)
661 """Base class for coordinate column, in degrees
670 output = df[self.
col] * 180 / np.pi
if self.
_radians else df[self.
col]
675 """Right Ascension, in degrees
681 super().
__init__(
'coord_ra', **kwargs)
684 return super().
__call__(catalog, **kwargs)
688 """Declination, in degrees
694 super().
__init__(
'coord_dec', **kwargs)
697 return super().
__call__(catalog, **kwargs)
701 """Compute the level 20 HtmIndex for the catalog.
705 This functor was implemented to satisfy requirements of old APDB interface
706 which required ``pixelId`` column in DiaObject
with HTM20 index. APDB
707 interface had migrated to
not need that information, but we keep this
708 class in case it may be useful for something else.
723 def computePixel(row):
732 return self.
pixelator.index(sphPoint.getVector())
734 return df.apply(computePixel, axis=1, result_type=
'reduce').astype(
'int64')
738 if not col.endswith(
'_instFlux'):
744 if not col.endswith(
'_instFluxErr'):
745 col +=
'_instFluxErr'
750 """Compute calibrated magnitude
752 Takes a `calib` argument, which returns the flux at mag=0
753 as `calib.getFluxMag0()`. If
not provided, then the default
754 `fluxMag0`
is 63095734448.0194, which
is default
for HSC.
755 This default should be removed
in DM-21955
757 This calculation hides warnings about invalid values
and dividing by zero.
759 As
for all functors, a `dataset`
and `filt` kwarg should be provided upon
760 initialization. Unlike the default `Functor`, however, the default dataset
761 for a `Mag`
is `
'meas'`, rather than `
'ref'`.
766 Name of flux column
from which to compute magnitude. Can be parseable
767 by `lsst.pipe.tasks.functors.fluxName` function---that
is, you can
pass
768 `
'modelfit_CModel'` instead of `
'modelfit_CModel_instFlux'`)
and it will
770 calib : `lsst.afw.image.calib.Calib` (optional)
771 Object that knows zero point.
773 _defaultDataset = 'meas'
778 if calib
is not None:
791 with warnings.catch_warnings():
792 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
793 warnings.filterwarnings(
'ignore',
r'divide by zero')
798 return f
'mag_{self.col}'
802 """Compute calibrated magnitude uncertainty
809 calib : `lsst.afw.image.calib.Calib` (optional)
810 Object that knows zero point.
815 if self.
calib is not None:
822 return [self.
col, self.
col +
'Err']
825 with warnings.catch_warnings():
826 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
827 warnings.filterwarnings(
'ignore',
r'divide by zero')
829 x = df[fluxErrCol] / df[fluxCol]
831 magErr = (2.5 / np.log(10.)) * np.sqrt(x*x + y*y)
836 return super().name +
'_err'
840 _defaultDataset =
'meas'
842 """Functor to calculate magnitude difference"""
854 with warnings.catch_warnings():
855 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
856 warnings.filterwarnings(
'ignore',
r'divide by zero')
857 return -2.5*np.log10(df[self.
col1]/df[self.
col2])
861 return f
'(mag_{self.col1} - mag_{self.col2})'
865 return f
'magDiff_{self.col1}_{self.col2}'
869 """Compute the color between two filters
871 Computes color by initializing two different `Mag`
872 functors based on the `col` and filters provided,
and
873 then returning the difference.
875 This
is enabled by the `_func` expecting a dataframe
with a
876 multilevel column index,
with both `
'band'`
and `
'column'`,
877 instead of just `
'column'`, which
is the `Functor` default.
878 This
is controlled by the `_dfLevels` attribute.
880 Also of note, the default dataset
for `Color`
is `forced_src
'`,
881 whereas for `Mag` it
is `
'meas'`.
886 Name of flux column
from which to compute; same
as would be passed to
890 Filters
from which to compute magnitude difference.
891 Color computed
is `
Mag(filt2) -
Mag(filt1)`.
893 _defaultDataset = 'forced_src'
894 _dfLevels = (
'band',
'column')
900 raise RuntimeError(
"Cannot compute Color for %s: %s - %s " % (col, filt2, filt1))
918 mag2 = self.mag2._func(df[self.filt2])
919 mag1 = self.mag1._func(df[self.filt1])
924 return [self.
mag1.col, self.
mag2.col]
931 return f
'{self.filt2} - {self.filt1} ({self.col})'
935 return f
"{self.col}_{self.filt2.replace('-', '')}m{self.filt1.replace('-', '')}"
939 name =
'Deconvolved Moments'
940 shortname =
'deconvolvedMoments'
941 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
942 "ext_shapeHSM_HsmSourceMoments_yy",
943 "base_SdssShape_xx",
"base_SdssShape_yy",
944 "ext_shapeHSM_HsmPsfMoments_xx",
945 "ext_shapeHSM_HsmPsfMoments_yy")
948 """Calculate deconvolved moments"""
949 if "ext_shapeHSM_HsmSourceMoments_xx" in df.columns:
950 hsm = df[
"ext_shapeHSM_HsmSourceMoments_xx"] + df[
"ext_shapeHSM_HsmSourceMoments_yy"]
952 hsm = np.ones(len(df))*np.nan
953 sdss = df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]
954 if "ext_shapeHSM_HsmPsfMoments_xx" in df.columns:
955 psf = df[
"ext_shapeHSM_HsmPsfMoments_xx"] + df[
"ext_shapeHSM_HsmPsfMoments_yy"]
960 raise RuntimeError(
'No psf shape parameter found in catalog')
962 return hsm.where(np.isfinite(hsm), sdss) - psf
966 """Functor to calculate SDSS trace radius size for sources"""
967 name =
"SDSS Trace Size"
968 shortname =
'sdssTrace'
969 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy")
972 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
977 """Functor to calculate SDSS trace radius size difference (%) between object and psf model"""
978 name =
"PSF - SDSS Trace Size"
979 shortname =
'psf_sdssTrace'
980 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy",
981 "base_SdssShape_psf_xx",
"base_SdssShape_psf_yy")
984 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
985 psfSize = np.sqrt(0.5*(df[
"base_SdssShape_psf_xx"] + df[
"base_SdssShape_psf_yy"]))
986 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
991 """Functor to calculate HSM trace radius size for sources"""
992 name =
'HSM Trace Size'
993 shortname =
'hsmTrace'
994 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
995 "ext_shapeHSM_HsmSourceMoments_yy")
998 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
999 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1004 """Functor to calculate HSM trace radius size difference (%) between object and psf model"""
1005 name =
'PSF - HSM Trace Size'
1006 shortname =
'psf_HsmTrace'
1007 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1008 "ext_shapeHSM_HsmSourceMoments_yy",
1009 "ext_shapeHSM_HsmPsfMoments_xx",
1010 "ext_shapeHSM_HsmPsfMoments_yy")
1012 def _func(self, df):
1013 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1014 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1015 psfSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmPsfMoments_xx"]
1016 + df[
"ext_shapeHSM_HsmPsfMoments_yy"]))
1017 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1022 name =
'HSM Psf FWHM'
1023 _columns = (
'ext_shapeHSM_HsmPsfMoments_xx',
'ext_shapeHSM_HsmPsfMoments_yy')
1026 SIGMA2FWHM = 2*np.sqrt(2*np.log(2))
1028 def _func(self, df):
1030 0.5*(df[
'ext_shapeHSM_HsmPsfMoments_xx'] + df[
'ext_shapeHSM_HsmPsfMoments_yy']))
1034 name =
"Distortion Ellipticity (e1)"
1035 shortname =
"Distortion"
1048 def _func(self, df):
1053 name =
"Ellipticity e2"
1065 def _func(self, df):
1081 def _func(self, df):
1086 """Computations using the stored localWcs.
1088 name = "LocalWcsOperations"
1103 """Compute the distance on the sphere from x2, y1 to x1, y1.
1111 cd11 : `pandas.Series`
1112 [1, 1] element of the local Wcs affine transform.
1113 cd11 : `pandas.Series`
1114 [1, 1] element of the local Wcs affine transform.
1115 cd12 : `pandas.Series`
1116 [1, 2] element of the local Wcs affine transform.
1117 cd21 : `pandas.Series`
1118 [2, 1] element of the local Wcs affine transform.
1119 cd22 : `pandas.Series`
1120 [2, 2] element of the local Wcs affine transform.
1125 RA and dec conversion of x
and y given the local Wcs. Returned
1126 units are
in radians.
1129 return (x * cd11 + y * cd12, x * cd21 + y * cd22)
1132 """Compute the local pixel scale conversion.
1136 ra1 : `pandas.Series`
1137 Ra of the first coordinate in radians.
1138 dec1 : `pandas.Series`
1139 Dec of the first coordinate
in radians.
1140 ra2 : `pandas.Series`
1141 Ra of the second coordinate
in radians.
1142 dec2 : `pandas.Series`
1143 Dec of the second coordinate
in radians.
1147 dist : `pandas.Series`
1148 Distance on the sphere
in radians.
1150 deltaDec = dec2 - dec1
1152 return 2 * np.arcsin(
1154 np.sin(deltaDec / 2) ** 2
1155 + np.cos(dec2) * np.cos(dec1) * np.sin(deltaRa / 2) ** 2))
1158 """Compute the distance on the sphere from x2, y1 to x1, y1.
1162 x1 : `pandas.Series`
1164 y1 : `pandas.Series`
1166 x2 : `pandas.Series`
1168 y2 : `pandas.Series`
1170 cd11 : `pandas.Series`
1171 [1, 1] element of the local Wcs affine transform.
1172 cd11 : `pandas.Series`
1173 [1, 1] element of the local Wcs affine transform.
1174 cd12 : `pandas.Series`
1175 [1, 2] element of the local Wcs affine transform.
1176 cd21 : `pandas.Series`
1177 [2, 1] element of the local Wcs affine transform.
1178 cd22 : `pandas.Series`
1179 [2, 2] element of the local Wcs affine transform.
1183 Distance : `pandas.Series`
1184 Arcseconds per pixel at the location of the local WC
1193 """Compute the local pixel scale from the stored CDMatrix.
1205 """Compute the local pixel to scale conversion in arcseconds.
1209 cd11 : `pandas.Series`
1210 [1, 1] element of the local Wcs affine transform in radians.
1211 cd11 : `pandas.Series`
1212 [1, 1] element of the local Wcs affine transform
in radians.
1213 cd12 : `pandas.Series`
1214 [1, 2] element of the local Wcs affine transform
in radians.
1215 cd21 : `pandas.Series`
1216 [2, 1] element of the local Wcs affine transform
in radians.
1217 cd22 : `pandas.Series`
1218 [2, 2] element of the local Wcs affine transform
in radians.
1222 pixScale : `pandas.Series`
1223 Arcseconds per pixel at the location of the local WC
1225 return 3600 * np.degrees(np.sqrt(np.fabs(cd11 * cd22 - cd12 * cd21)))
1227 def _func(self, df):
1235 """Convert a value in units pixels to units arcseconds.
1254 return f
"{self.col}_asArcseconds"
1264 def _func(self, df):
1272 """Convert a value in units pixels squared to units arcseconds squared.
1291 return f
"{self.col}_asArcsecondsSq"
1301 def _func(self, df):
1306 return df[self.
col] * pixScale * pixScale
1310 name =
'Reference Band'
1311 shortname =
'refBand'
1315 return [
"merge_measurement_i",
1316 "merge_measurement_r",
1317 "merge_measurement_z",
1318 "merge_measurement_y",
1319 "merge_measurement_g",
1320 "merge_measurement_u"]
1322 def _func(self, df: pd.DataFrame) -> pd.Series:
1323 def getFilterAliasName(row):
1325 colName = row.idxmax()
1326 return colName.replace(
'merge_measurement_',
'')
1330 columns = [col
for col
in self.
columnscolumns if col
in df.columns]
1332 return df[columns].apply(getFilterAliasName, axis=1,
1333 result_type=
'reduce').astype(
'object')
1338 AB_FLUX_SCALE = (0 * u.ABmag).to_value(u.nJy)
1339 LOG_AB_FLUX_SCALE = 12.56
1340 FIVE_OVER_2LOG10 = 1.085736204758129569
1344 def __init__(self, colFlux, colFluxErr=None, calib=None, **kwargs):
1350 if calib
is not None:
1364 return f
'mag_{self.col}'
1368 if np.abs(a) < np.abs(b):
1373 return np.abs(a) * np.sqrt(1. + q*q)
1379 with warnings.catch_warnings():
1380 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1381 warnings.filterwarnings(
'ignore',
r'divide by zero')
1382 return -2.5 * np.log10(dn/fluxMag0)
1385 retVal = self.
vhypot(dn * fluxMag0Err, dnErr * fluxMag0)
1390 retVal = self.
dn2fluxErr(dn, dnErr, fluxMag0, fluxMag0Err) / self.
dn2flux(dn, fluxMag0)
1395 def _func(self, df):
1404 def _func(self, df):
1406 return pd.Series(retArr, index=df.index)
1410 """Base class for calibrating the specified instrument flux column using
1411 the local photometric calibration.
1416 Name of the instrument flux column.
1417 instFluxErrCol : `str`
1418 Name of the assocated error columns for ``instFluxCol``.
1419 photoCalibCol : `str`
1420 Name of local calibration column.
1421 photoCalibErrCol : `str`
1422 Error associated
with ``photoCalibCol``
1429 logNJanskyToAB = (1 * u.nJy).to_value(u.ABmag)
1444 """Convert instrument flux to nanojanskys.
1448 instFlux : `numpy.ndarray` or `pandas.Series`
1449 Array of instrument flux measurements
1450 localCalib : `numpy.ndarray`
or `pandas.Series`
1451 Array of local photometric calibration estimates.
1455 calibFlux : `numpy.ndarray`
or `pandas.Series`
1456 Array of calibrated flux measurements.
1458 return instFlux * localCalib
1461 """Convert instrument flux to nanojanskys.
1465 instFlux : `numpy.ndarray` or `pandas.Series`
1466 Array of instrument flux measurements
1467 instFluxErr : `numpy.ndarray`
or `pandas.Series`
1468 Errors on associated ``instFlux`` values
1469 localCalib : `numpy.ndarray`
or `pandas.Series`
1470 Array of local photometric calibration estimates.
1471 localCalibErr : `numpy.ndarray`
or `pandas.Series`
1472 Errors on associated ``localCalib`` values
1476 calibFluxErr : `numpy.ndarray`
or `pandas.Series`
1477 Errors on calibrated flux measurements.
1479 return np.hypot(instFluxErr * localCalib, instFlux * localCalibErr)
1482 """Convert instrument flux to nanojanskys.
1486 instFlux : `numpy.ndarray` or `pandas.Series`
1487 Array of instrument flux measurements
1488 localCalib : `numpy.ndarray`
or `pandas.Series`
1489 Array of local photometric calibration estimates.
1493 calibMag : `numpy.ndarray`
or `pandas.Series`
1494 Array of calibrated AB magnitudes.
1499 """Convert instrument flux err to nanojanskys.
1503 instFlux : `numpy.ndarray` or `pandas.Series`
1504 Array of instrument flux measurements
1505 instFluxErr : `numpy.ndarray`
or `pandas.Series`
1506 Errors on associated ``instFlux`` values
1507 localCalib : `numpy.ndarray`
or `pandas.Series`
1508 Array of local photometric calibration estimates.
1509 localCalibErr : `numpy.ndarray`
or `pandas.Series`
1510 Errors on associated ``localCalib`` values
1514 calibMagErr: `numpy.ndarray`
or `pandas.Series`
1515 Error on calibrated AB magnitudes.
1522 """Compute calibrated fluxes using the local calibration value."""
1530 return f
'flux_{self.instFluxCol}'
1532 def _func(self, df):
1537 """Compute calibrated flux errors using the local calibration value."""
1546 return f
'fluxErr_{self.instFluxCol}'
1548 def _func(self, df):
1554 """Compute absolute mean of dipole fluxes.
1560 LocalDipoleMeanFluxErr
1562 LocalDipoleDiffFluxErr
1592 return f
'dipMeanFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1594 def _func(self, df):
1600 """Compute the error on the absolute mean of dipole fluxes.
1608 LocalDipoleDiffFluxErr
1622 return f
'dipMeanFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1624 def _func(self, df):
1633 """Compute the absolute difference of dipole fluxes.
1635 Value is (abs(pos) - abs(neg))
1642 LocalDipoleMeanFluxErr
1643 LocalDipoleDiffFluxErr
1654 return f
'dipDiffFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1656 def _func(self, df):
1662 """Compute the error on the absolute difference of dipole fluxes.
1669 LocalDipoleMeanFluxErr
1684 return f
'dipDiffFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1686 def _func(self, df):
1695 """Compute E(B-V) from dustmaps.sfd
1697 _defaultDataset = 'ref'
1703 from dustmaps.sfd
import SFDQuery
1704 self.
_columns = [
'coord_ra',
'coord_dec']
1708 def _func(self, df):
1709 coords = SkyCoord(df[
'coord_ra'].values * u.rad, df[
'coord_dec'].values * u.rad)
1710 ebv = self.
sfd(coords)
1713 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)