22__all__ = [
"init_fromDict",
"Functor",
"CompositeFunctor",
"mag_aware_eval",
23 "CustomFunctor",
"Column",
"Index",
"CoordColumn",
"RAColumn",
24 "DecColumn",
"HtmIndex20",
"fluxName",
"fluxErrName",
"Mag",
25 "MagErr",
"MagDiff",
"Color",
"DeconvolvedMoments",
"SdssTraceSize",
26 "PsfSdssTraceSizeDiff",
"HsmTraceSize",
"PsfHsmTraceSizeDiff",
27 "HsmFwhm",
"E1",
"E2",
"RadiusFromQuadrupole",
"LocalWcs",
28 "ComputePixelScale",
"ConvertPixelToArcseconds",
29 "ConvertPixelSqToArcsecondsSq",
"ReferenceBand",
"Photometry",
30 "NanoJansky",
"NanoJanskyErr",
"LocalPhotometry",
"LocalNanojansky",
31 "LocalNanojanskyErr",
"LocalDipoleMeanFlux",
32 "LocalDipoleMeanFluxErr",
"LocalDipoleDiffFlux",
33 "LocalDipoleDiffFluxErr",
"Ebv",
38from itertools
import product
45import astropy.units
as u
46from astropy.coordinates
import SkyCoord
49from lsst.utils.introspection
import get_full_type_name
50from lsst.daf.butler
import DeferredDatasetHandle
57 typeKey='functor', name=None):
58 """Initialize an object defined in a dictionary.
60 The object needs to be importable as f'{basePath}.{initDict[typeKey]}'.
61 The positional and keyword arguments (if any) are contained in "args" and
62 "kwargs" entries in the dictionary, respectively.
63 This is used in `~lsst.pipe.tasks.functors.CompositeFunctor.from_yaml` to
64 initialize a composite functor from a specification in a YAML file.
69 Dictionary describing object's initialization.
70 Must contain an entry keyed by ``typeKey`` that is the name of the
71 object, 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 (relative to
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
98 The `__call__` method accepts either a `~pandas.DataFrame` object or a
99 `~lsst.daf.butler.DeferredDatasetHandle` or
100 `~lsst.pipe.base.InMemoryDatasetHandle`, and returns the
101 result of the calculation as a single column.
102 Each functor defines what columns are needed for the calculation, and only
103 these columns are read from the dataset handle.
105 The action of `__call__` consists of two steps: first, loading the
106 necessary columns from disk into memory as a `~pandas.DataFrame` object;
107 and second, performing the computation on this DataFrame and returning the
110 To define a new `Functor`, a subclass must define a `_func` method,
111 that takes a `~pandas.DataFrame` and returns result in a `~pandas.Series`.
112 In addition, it must define the following attributes:
114 * `_columns`: The columns necessary to perform the calculation
115 * `name`: A name appropriate for a figure axis label
116 * `shortname`: A name appropriate for use as a dictionary key
118 On initialization, a `Functor` should declare what band (``filt`` kwarg)
119 and dataset (e.g. ``'ref'``, ``'meas'``, ``'forced_src'``) it is intended
121 This enables the `_get_data` method to extract the proper columns from the
123 If not specified, the dataset will fall back on the `_defaultDataset`
125 If band is not specified and ``dataset`` is anything other than ``'ref'``,
126 then an error will be raised when trying to perform the calculation.
128 Originally, `Functor` was set up to expect datasets formatted like the
129 ``deepCoadd_obj`` dataset; that is, a DataFrame with a multi-level column
130 index, with the levels of the column index being ``band``, ``dataset``, and
132 It has since been generalized to apply to DataFrames without multi-level
133 indices and multi-level indices with just ``dataset`` and ``column``
135 In addition, the `_get_data` method that reads the columns from the
136 underlying data will return a DataFrame with column index levels defined by
137 the `_dfLevels` attribute; by default, this is ``column``.
139 The `_dfLevels` attributes should generally not need to be changed, unless
140 `_func` needs columns from multiple filters or datasets to do the
142 An example of this is the `~lsst.pipe.tasks.functors.Color` functor, for
143 which `_dfLevels = ('band', 'column')`, and `_func` expects the DataFrame
144 it gets to have those levels in the column index.
149 Band upon which to do the calculation.
152 Dataset upon which to do the calculation (e.g., 'ref', 'meas',
156 _defaultDataset =
'ref'
157 _dfLevels = (
'column',)
158 _defaultNoDup =
False
160 def __init__(self, filt=None, dataset=None, noDup=None):
164 self.
log = logging.getLogger(type(self).__name__)
168 """Do not explode by band if used on object table."""
169 if self.
_noDup is not None:
176 """Columns required to perform calculation."""
177 if not hasattr(self,
'_columns'):
178 raise NotImplementedError(
'Must define columns property or _columns attribute')
182 """Gets the names of the column index levels.
184 This should only be called in the context of a multilevel table.
189 The data to be read, can be a
190 `~lsst.daf.butler.DeferredDatasetHandle` or
191 `~lsst.pipe.base.InMemoryDatasetHandle`.
192 columnIndex (optional): pandas `~pandas.Index` object
193 If not passed, then it is read from the
194 `~lsst.daf.butler.DeferredDatasetHandle`
195 for `~lsst.pipe.base.InMemoryDatasetHandle`.
197 if columnIndex
is None:
198 columnIndex = data.get(component=
"columns")
199 return columnIndex.names
202 """Gets the content of each of the column levels for a multilevel
205 if columnIndex
is None:
206 columnIndex = data.get(component=
"columns")
208 columnLevels = columnIndex.names
210 level: list(np.unique(np.array([c
for c
in columnIndex])[:, i]))
211 for i, level
in enumerate(columnLevels)
213 return columnLevelNames
216 """Converts dictionary column specficiation to a list of columns."""
220 for i, lev
in enumerate(columnLevels):
222 if isinstance(colDict[lev], str):
223 new_colDict[lev] = [colDict[lev]]
225 new_colDict[lev] = colDict[lev]
227 new_colDict[lev] = columnIndex.levels[i]
229 levelCols = [new_colDict[lev]
for lev
in columnLevels]
230 cols = list(product(*levelCols))
231 colsAvailable = [col
for col
in cols
if col
in columnIndex]
235 """Returns columns needed by functor from multilevel dataset.
237 To access tables with multilevel column structure, the
238 `~lsst.daf.butler.DeferredDatasetHandle` or
239 `~lsst.pipe.base.InMemoryDatasetHandle` needs to be passed
240 either a list of tuples or a dictionary.
245 The data as either `~lsst.daf.butler.DeferredDatasetHandle`, or
246 `~lsst.pipe.base.InMemoryDatasetHandle`.
247 columnIndex (optional): pandas `~pandas.Index` object
248 Either passed or read in from
249 `~lsst.daf.butler.DeferredDatasetHandle`.
250 `returnTuple` : `bool`
251 If true, then return a list of tuples rather than the column
252 dictionary specification.
253 This is set to `True` by `CompositeFunctor` in order to be able to
254 combine columns from the various component functors.
257 if not isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
258 raise RuntimeError(f
"Unexpected data type. Got {get_full_type_name(data)}.")
260 if columnIndex
is None:
261 columnIndex = data.get(component=
"columns")
267 columnDict = {
'column': self.
columns,
269 if self.
filt is None:
271 if "band" in columnLevels:
273 columnDict[
"band"] = columnLevelNames[
"band"][0]
275 raise ValueError(f
"'filt' not set for functor {self.name}"
276 f
"(dataset {self.dataset}) "
278 "contains multiple filters in column index. "
279 "Set 'filt' or set 'dataset' to 'ref'.")
281 columnDict[
'band'] = self.
filt
284 return self.
_colsFromDict(columnDict, columnIndex=columnIndex)
289 raise NotImplementedError(
'Must define calculation on DataFrame')
292 """Return columnIndex."""
294 if isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
295 return data.get(component=
"columns")
300 """Retrieve DataFrame necessary for calculation.
302 The data argument can be a `~pandas.DataFrame`, a
303 `~lsst.daf.butler.DeferredDatasetHandle`, or
304 an `~lsst.pipe.base.InMemoryDatasetHandle`.
306 Returns a DataFrame upon which `self._func` can act.
310 if isinstance(data, pd.DataFrame):
311 _data = InMemoryDatasetHandle(data, storageClass=
"DataFrame")
312 elif isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
315 raise RuntimeError(f
"Unexpected type provided for data. Got {get_full_type_name(data)}.")
320 is_multiLevel = isinstance(columnIndex, pd.MultiIndex)
329 df = _data.get(parameters={
"columns": columns})
338 levelsToDrop = [n
for n
in df.columns.names
if n
not in self.
_dfLevels]
339 df.columns = df.columns.droplevel(levelsToDrop)
348 vals = self.
_func(df)
349 except Exception
as e:
350 self.
log.error(
"Exception in %s call: %s: %s", self.
namename, type(e).__name__, e)
358 """Computes difference between functor called on two different
359 DataFrame/Handle objects.
361 return self(data1, **kwargs) - self(data2, **kwargs)
364 return pd.Series(np.full(len(df), np.nan), index=df.index)
368 """Full name of functor (suitable for figure labels)."""
369 return NotImplementedError
373 """Short name of functor (suitable for column name/dict key)."""
378 """Perform multiple calculations at once on a catalog.
380 The role of a `CompositeFunctor` is to group together computations from
382 Instead of returning `~pandas.Series` a `CompositeFunctor` returns a
383 `~pandas.DataFrame`, with the column names being the keys of ``funcDict``.
385 The `columns` attribute of a `CompositeFunctor` is the union of all columns
386 in all the component functors.
388 A `CompositeFunctor` does not use a `_func` method itself; rather, when a
389 `CompositeFunctor` is called, all its columns are loaded at once, and the
390 resulting DataFrame is passed to the `_func` method of each component
392 This has the advantage of only doing I/O (reading from parquet file) once,
393 and works because each individual `_func` method of each component functor
394 does not care if there are *extra* columns in the DataFrame being passed;
395 only that it must contain *at least* the `columns` it expects.
397 An important and useful class method is `from_yaml`, which takes as an
398 argument the path to a YAML file specifying a collection of functors.
402 funcs : `dict` or `list`
403 Dictionary or list of functors.
404 If a list, then it will be converted into a dictonary according to the
405 `.shortname` attribute of each functor.
408 name =
"CompositeFunctor"
412 if type(funcs) == dict:
415 self.
funcDict = {f.shortname: f
for f
in funcs}
433 """Update the functor with new functors."""
434 if isinstance(new, dict):
436 elif isinstance(new, CompositeFunctor):
439 raise TypeError(
'Can only update with dictionary or CompositeFunctor.')
447 return list(
set([x
for y
in [f.columns
for f
in self.
funcDict.values()]
for x
in y]))
457 f.multilevelColumns(data, returnTuple=
True, **kwargs)
for f
in self.
funcDict.values()
465 """Apply the functor to the data table.
470 The data represented as `~lsst.daf.butler.DeferredDatasetHandle`,
471 `~lsst.pipe.base.InMemoryDatasetHandle`, or `~pandas.DataFrame`.
472 The table or a pointer to a table on disk from which columns can
475 if isinstance(data, pd.DataFrame):
476 _data = InMemoryDatasetHandle(data, storageClass=
"DataFrame")
477 elif isinstance(data, (DeferredDatasetHandle, InMemoryDatasetHandle)):
480 raise RuntimeError(f
"Unexpected type provided for data. Got {get_full_type_name(data)}.")
484 if isinstance(columnIndex, pd.MultiIndex):
486 df = _data.get(parameters={
"columns": columns})
491 subdf = f._setLevels(
492 df[f.multilevelColumns(_data, returnTuple=
True, columnIndex=columnIndex)]
494 valDict[k] = f._func(subdf)
495 except Exception
as e:
497 "Exception in %s (funcs: %s) call: %s",
503 valDict[k] = f.fail(subdf)
513 for name, colVal
in valDict.items():
514 if len(colVal.shape) != 1:
515 raise RuntimeError(
"Transformed column '%s' is not the shape of a column. "
516 "It is shaped %s and type %s." % (name, colVal.shape, type(colVal)))
519 valDf = pd.concat(valDict, axis=1)
521 print([(k, type(v))
for k, v
in valDict.items()])
524 if kwargs.get(
'dropna',
False):
525 valDf = valDf.dropna(how=
'any')
531 if renameRules
is None:
533 for old, new
in renameRules:
534 if col.startswith(old):
535 col = col.replace(old, new)
541 filename = os.path.expandvars(filename)
542 with open(filename)
as f:
543 translationDefinition = yaml.safe_load(f)
545 return cls.
from_yaml(translationDefinition, **kwargs)
550 for func, val
in translationDefinition[
'funcs'].
items():
553 if 'flag_rename_rules' in translationDefinition:
554 renameRules = translationDefinition[
'flag_rename_rules']
558 if 'calexpFlags' in translationDefinition:
559 for flag
in translationDefinition[
'calexpFlags']:
560 funcs[cls.
renameCol(flag, renameRules)] =
Column(flag, dataset=
'calexp')
562 if 'refFlags' in translationDefinition:
563 for flag
in translationDefinition[
'refFlags']:
566 if 'forcedFlags' in translationDefinition:
567 for flag
in translationDefinition[
'forcedFlags']:
568 funcs[cls.
renameCol(flag, renameRules)] =
Column(flag, dataset=
'forced_src')
570 if 'flags' in translationDefinition:
571 for flag
in translationDefinition[
'flags']:
574 return cls(funcs, **kwargs)
578 """Evaluate an expression on a DataFrame, knowing what the 'mag' function
581 Builds on `pandas.DataFrame.eval`, which parses and executes math on
586 df : ~pandas.DataFrame
587 DataFrame on which to evaluate expression.
593 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>)/log(10)', expr)
594 val = df.eval(expr_new)
595 except Exception
as e:
596 log.error(
"Exception in mag_aware_eval: %s: %s", type(e).__name__, e)
597 expr_new = re.sub(
r'mag\((\w+)\)',
r'-2.5*log(\g<1>_instFlux)/log(10)', expr)
598 val = df.eval(expr_new)
603 """Arbitrary computation on a catalog.
605 Column names (and thus the columns to be loaded from catalog) are found by
606 finding all words and trying to ignore all "math-y" words.
611 Expression to evaluate, to be parsed and executed by
612 `~lsst.pipe.tasks.functors.mag_aware_eval`.
614 _ignore_words = (
'mag',
'sin',
'cos',
'exp',
'log',
'sqrt')
626 flux_cols = re.findall(
r'mag\(\s*(\w+)\s*\)', self.
expr)
628 cols = [c
for c
in re.findall(
r'[a-zA-Z_]+', self.
expr)
if c
not in self.
_ignore_words]
631 if not re.search(
'_instFlux$', c):
632 cols.append(f
'{c}_instFlux')
637 return list(
set([c
for c
in cols
if c
not in not_a_col]))
644 """Get column with a specified name."""
663 """Return the value of the index for each object."""
665 columns = [
'coord_ra']
666 _defaultDataset =
'ref'
670 return pd.Series(df.index, index=df.index)
674 """Base class for coordinate column, in degrees."""
683 output = df[self.
col] * 180 / np.pi
if self.
_radians else df[self.
col]
688 """Right Ascension, in degrees."""
693 super().
__init__(
'coord_ra', **kwargs)
696 return super().
__call__(catalog, **kwargs)
700 """Declination, in degrees."""
705 super().
__init__(
'coord_dec', **kwargs)
708 return super().
__call__(catalog, **kwargs)
712 """Uncertainty in Right Ascension, in degrees."""
717 super().
__init__(
'coord_raErr', **kwargs)
721 """Uncertainty in declination, in degrees."""
726 super().
__init__(
'coord_decErr', **kwargs)
730 """Coordinate covariance column, in degrees."""
736 super().
__init__(
'coord_ra_dec_Cov', **kwargs)
741 output = df[self.
col]*(180/np.pi)**2
if self.
_radians else df[self.
col]
746 """Compute the level 20 HtmIndex for the catalog.
750 This functor was implemented to satisfy requirements of old APDB interface
751 which required the ``pixelId`` column in DiaObject with HTM20 index.
752 The APDB interface had migrated to not need that information, but we keep
753 this class in case it may be useful for something else.
768 def computePixel(row):
777 return self.
pixelator.index(sphPoint.getVector())
779 return df.apply(computePixel, axis=1, result_type=
'reduce').astype(
'int64')
783 """Append _instFlux to the column name if it doesn't have it already."""
784 if not col.endswith(
'_instFlux'):
790 """Append _instFluxErr to the column name if it doesn't have it already."""
791 if not col.endswith(
'_instFluxErr'):
792 col +=
'_instFluxErr'
797 """Compute calibrated magnitude.
799 Returns the flux at mag=0.
800 The default ``fluxMag0`` is 63095734448.0194, which is default for HSC.
801 TO DO: This default should be made configurable in DM-21955.
803 This calculation hides warnings about invalid values and dividing by zero.
805 As with all functors, a ``dataset`` and ``filt`` kwarg should be provided
807 Unlike the default `Functor`, however, the default dataset for a `Mag` is
808 ``'meas'``, rather than ``'ref'``.
813 Name of flux column from which to compute magnitude.
814 Can be parseable by the `~lsst.pipe.tasks.functors.fluxName` function;
815 that is, you can pass ``'modelfit_CModel'`` instead of
816 ``'modelfit_CModel_instFlux'``, and it will understand.
818 _defaultDataset =
'meas'
832 with warnings.catch_warnings():
833 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
834 warnings.filterwarnings(
'ignore',
r'divide by zero')
839 return f
'mag_{self.col}'
843 """Compute calibrated magnitude uncertainty.
848 Name of the flux column.
861 with warnings.catch_warnings():
862 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
863 warnings.filterwarnings(
'ignore',
r'divide by zero')
865 x = df[fluxErrCol] / df[fluxCol]
867 magErr = (2.5 / np.log(10.)) * np.sqrt(x*x + y*y)
872 return super().name +
'_err'
876 """Functor to calculate magnitude difference."""
877 _defaultDataset =
'meas'
889 with warnings.catch_warnings():
890 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
891 warnings.filterwarnings(
'ignore',
r'divide by zero')
892 return -2.5*np.log10(df[self.
col1]/df[self.
col2])
896 return f
'(mag_{self.col1} - mag_{self.col2})'
900 return f
'magDiff_{self.col1}_{self.col2}'
904 """Compute the color between two filters.
906 Computes color by initializing two different `Mag` functors based on the
907 ``col`` and filters provided, and then returning the difference.
909 This is enabled by the `_func` method expecting a DataFrame with a
910 multilevel column index, with both ``'band'`` and ``'column'``, instead of
911 just ``'column'``, which is the `Functor` default.
912 This is controlled by the `_dfLevels` attribute.
914 Also of note, the default dataset for `Color` is ``forced_src'``, whereas
915 for `Mag` it is ``'meas'``.
920 Name of the flux column from which to compute; same as would be passed
921 to `~lsst.pipe.tasks.functors.Mag`.
924 Filters from which to compute magnitude difference.
925 Color computed is ``Mag(filt2) - Mag(filt1)``.
927 _defaultDataset =
'forced_src'
928 _dfLevels = (
'band',
'column')
934 raise RuntimeError(
"Cannot compute Color for %s: %s - %s " % (col, filt2, filt1))
952 mag2 = self.mag2.
_func(df[self.filt2])
953 mag1 = self.mag1.
_func(df[self.filt1])
958 return [self.
mag1.col, self.
mag2.col]
965 return f
'{self.filt2} - {self.filt1} ({self.col})'
969 return f
"{self.col}_{self.filt2.replace('-', '')}m{self.filt1.replace('-', '')}"
973 """This functor subtracts the trace of the PSF second moments from the
974 trace of the second moments of the source.
976 If the HsmShapeAlgorithm measurement is valid, then these will be used for
978 Otherwise, the SdssShapeAlgorithm measurements will be used.
980 name =
'Deconvolved Moments'
981 shortname =
'deconvolvedMoments'
982 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
983 "ext_shapeHSM_HsmSourceMoments_yy",
984 "base_SdssShape_xx",
"base_SdssShape_yy",
985 "ext_shapeHSM_HsmPsfMoments_xx",
986 "ext_shapeHSM_HsmPsfMoments_yy")
989 """Calculate deconvolved moments."""
990 if "ext_shapeHSM_HsmSourceMoments_xx" in df.columns:
991 hsm = df[
"ext_shapeHSM_HsmSourceMoments_xx"] + df[
"ext_shapeHSM_HsmSourceMoments_yy"]
993 hsm = np.ones(len(df))*np.nan
994 sdss = df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]
995 if "ext_shapeHSM_HsmPsfMoments_xx" in df.columns:
996 psf = df[
"ext_shapeHSM_HsmPsfMoments_xx"] + df[
"ext_shapeHSM_HsmPsfMoments_yy"]
1001 raise RuntimeError(
'No psf shape parameter found in catalog')
1003 return hsm.where(np.isfinite(hsm), sdss) - psf
1007 """Functor to calculate the SDSS trace radius size for sources.
1009 The SDSS trace radius size is a measure of size equal to the square root of
1010 half of the trace of the second moments tensor measured with the
1011 SdssShapeAlgorithm plugin.
1012 This has units of pixels.
1014 name =
"SDSS Trace Size"
1015 shortname =
'sdssTrace'
1016 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy")
1019 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1024 """Functor to calculate the SDSS trace radius size difference (%) between
1025 the object and the PSF model.
1031 name =
"PSF - SDSS Trace Size"
1032 shortname =
'psf_sdssTrace'
1033 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy",
1034 "base_SdssShape_psf_xx",
"base_SdssShape_psf_yy")
1037 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1038 psfSize = np.sqrt(0.5*(df[
"base_SdssShape_psf_xx"] + df[
"base_SdssShape_psf_yy"]))
1039 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1044 """Functor to calculate the HSM trace radius size for sources.
1046 The HSM trace radius size is a measure of size equal to the square root of
1047 half of the trace of the second moments tensor measured with the
1048 HsmShapeAlgorithm plugin.
1049 This has units of pixels.
1051 name =
'HSM Trace Size'
1052 shortname =
'hsmTrace'
1053 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1054 "ext_shapeHSM_HsmSourceMoments_yy")
1057 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1058 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1063 """Functor to calculate the HSM trace radius size difference (%) between
1064 the object and the PSF model.
1070 name =
'PSF - HSM Trace Size'
1071 shortname =
'psf_HsmTrace'
1072 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1073 "ext_shapeHSM_HsmSourceMoments_yy",
1074 "ext_shapeHSM_HsmPsfMoments_xx",
1075 "ext_shapeHSM_HsmPsfMoments_yy")
1078 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1079 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1080 psfSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmPsfMoments_xx"]
1081 + df[
"ext_shapeHSM_HsmPsfMoments_yy"]))
1082 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1087 """Functor to calculate the PSF FWHM with second moments measured from the
1088 HsmShapeAlgorithm plugin.
1090 This is in units of arcseconds, and assumes the hsc_rings_v1 skymap pixel
1091 scale of 0.168 arcseconds/pixel.
1095 This conversion assumes the PSF is Gaussian, which is not always the case.
1097 name =
'HSM Psf FWHM'
1098 _columns = (
'ext_shapeHSM_HsmPsfMoments_xx',
'ext_shapeHSM_HsmPsfMoments_yy')
1101 SIGMA2FWHM = 2*np.sqrt(2*np.log(2))
1105 0.5*(df[
'ext_shapeHSM_HsmPsfMoments_xx'] + df[
'ext_shapeHSM_HsmPsfMoments_yy']))
1109 r"""Calculate :math:`e_1` ellipticity component for sources, defined as:
1112 e_1 &= (I_{xx}-I_{yy})/(I_{xx}+I_{yy})
1118 name =
"Distortion Ellipticity (e1)"
1119 shortname =
"Distortion"
1137 r"""Calculate :math:`e_2` ellipticity component for sources, defined as:
1140 e_2 &= 2I_{xy}/(I_{xx}+I_{yy})
1146 name =
"Ellipticity e2"
1163 """Calculate the radius from the quadrupole moments.
1165 This returns the fourth root of the determinant of the second moments
1166 tensor, which has units of pixels.
1189 """Computations using the stored localWcs."""
1190 name =
"LocalWcsOperations"
1205 """Compute the distance on the sphere from x2, y1 to x1, y1.
1209 x : `~pandas.Series`
1211 y : `~pandas.Series`
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.
1228 Returned 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)))
1337 """Convert a value in units of pixels to units of arcseconds."""
1355 return f
"{self.col}_asArcseconds"
1373 """Convert a value in units of pixels squared to units of arcseconds
1393 return f
"{self.col}_asArcsecondsSq"
1408 return df[self.
col] * pixScale * pixScale
1412 """Return the band used to seed multiband forced photometry.
1414 This functor is to be used on Object tables.
1415 It converts the boolean merge_measurements_{band} columns into a single
1416 string representing the first band for which merge_measurements_{band}
1419 Assumes the default priority order of i, r, z, y, g, u.
1421 name =
'Reference Band'
1422 shortname =
'refBand'
1426 return [
"merge_measurement_i",
1427 "merge_measurement_r",
1428 "merge_measurement_z",
1429 "merge_measurement_y",
1430 "merge_measurement_g",
1431 "merge_measurement_u"]
1433 def _func(self, df: pd.DataFrame) -> pd.Series:
1434 def getFilterAliasName(row):
1436 colName = row.idxmax()
1437 return colName.replace(
'merge_measurement_',
'')
1441 columns = [col
for col
in self.
columnscolumns if col
in df.columns]
1443 return df[columns].apply(getFilterAliasName, axis=1,
1444 result_type=
'reduce').astype(
'object')
1448 """Base class for Object table calibrated fluxes and magnitudes."""
1450 AB_FLUX_SCALE = (0 * u.ABmag).to_value(u.nJy)
1451 LOG_AB_FLUX_SCALE = 12.56
1452 FIVE_OVER_2LOG10 = 1.085736204758129569
1456 def __init__(self, colFlux, colFluxErr=None, **kwargs):
1472 return f
'mag_{self.col}'
1476 """Compute sqrt(a^2 + b^2) without under/overflow."""
1477 if np.abs(a) < np.abs(b):
1482 return np.abs(a) * np.sqrt(1. + q*q)
1485 """Convert instrumental flux to nanojanskys."""
1489 """Convert instrumental flux to AB magnitude."""
1490 with warnings.catch_warnings():
1491 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1492 warnings.filterwarnings(
'ignore',
r'divide by zero')
1493 return -2.5 * np.log10(dn/fluxMag0)
1496 """Convert instrumental flux error to nanojanskys."""
1497 retVal = self.
vhypot(dn * fluxMag0Err, dnErr * fluxMag0)
1502 """Convert instrumental flux error to AB magnitude error."""
1503 retVal = self.
dn2fluxErr(dn, dnErr, fluxMag0, fluxMag0Err) / self.
dn2flux(dn, fluxMag0)
1508 """Convert instrumental flux to nanojanskys."""
1514 """Convert instrumental flux error to nanojanskys."""
1521 return pd.Series(retArr, index=df.index)
1525 """Base class for calibrating the specified instrument flux column using
1526 the local photometric calibration.
1531 Name of the instrument flux column.
1532 instFluxErrCol : `str`
1533 Name of the assocated error columns for ``instFluxCol``.
1534 photoCalibCol : `str`
1535 Name of local calibration column.
1536 photoCalibErrCol : `str`
1537 Error associated with ``photoCalibCol``
1544 logNJanskyToAB = (1 * u.nJy).to_value(u.ABmag)
1559 """Convert instrument flux to nanojanskys.
1563 instFlux : `~numpy.ndarray` or `~pandas.Series`
1564 Array of instrument flux measurements.
1565 localCalib : `~numpy.ndarray` or `~pandas.Series`
1566 Array of local photometric calibration estimates.
1570 calibFlux : `~numpy.ndarray` or `~pandas.Series`
1571 Array of calibrated flux measurements.
1573 return instFlux * localCalib
1576 """Convert instrument flux to nanojanskys.
1580 instFlux : `~numpy.ndarray` or `~pandas.Series`
1581 Array of instrument flux measurements.
1582 instFluxErr : `~numpy.ndarray` or `~pandas.Series`
1583 Errors on associated ``instFlux`` values.
1584 localCalib : `~numpy.ndarray` or `~pandas.Series`
1585 Array of local photometric calibration estimates.
1586 localCalibErr : `~numpy.ndarray` or `~pandas.Series`
1587 Errors on associated ``localCalib`` values.
1591 calibFluxErr : `~numpy.ndarray` or `~pandas.Series`
1592 Errors on calibrated flux measurements.
1594 return np.hypot(instFluxErr * localCalib, instFlux * localCalibErr)
1597 """Convert instrument flux to nanojanskys.
1601 instFlux : `~numpy.ndarray` or `~pandas.Series`
1602 Array of instrument flux measurements.
1603 localCalib : `~numpy.ndarray` or `~pandas.Series`
1604 Array of local photometric calibration estimates.
1608 calibMag : `~numpy.ndarray` or `~pandas.Series`
1609 Array of calibrated AB magnitudes.
1614 """Convert instrument flux err to nanojanskys.
1618 instFlux : `~numpy.ndarray` or `~pandas.Series`
1619 Array of instrument flux measurements.
1620 instFluxErr : `~numpy.ndarray` or `~pandas.Series`
1621 Errors on associated ``instFlux`` values.
1622 localCalib : `~numpy.ndarray` or `~pandas.Series`
1623 Array of local photometric calibration estimates.
1624 localCalibErr : `~numpy.ndarray` or `~pandas.Series`
1625 Errors on associated ``localCalib`` values.
1629 calibMagErr: `~numpy.ndarray` or `~pandas.Series`
1630 Error on calibrated AB magnitudes.
1637 """Compute calibrated fluxes using the local calibration value.
1639 This returns units of nanojanskys.
1648 return f
'flux_{self.instFluxCol}'
1655 """Compute calibrated flux errors using the local calibration value.
1657 This returns units of nanojanskys.
1667 return f
'fluxErr_{self.instFluxCol}'
1675 """Compute absolute mean of dipole fluxes.
1681 LocalDipoleMeanFluxErr
1683 LocalDipoleDiffFluxErr
1713 return f
'dipMeanFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1721 """Compute the error on the absolute mean of dipole fluxes.
1729 LocalDipoleDiffFluxErr
1743 return f
'dipMeanFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1754 """Compute the absolute difference of dipole fluxes.
1756 Calculated value is (abs(pos) - abs(neg)).
1763 LocalDipoleMeanFluxErr
1764 LocalDipoleDiffFluxErr
1775 return f
'dipDiffFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1783 """Compute the error on the absolute difference of dipole fluxes.
1790 LocalDipoleMeanFluxErr
1805 return f
'dipDiffFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1816 """Compute E(B-V) from dustmaps.sfd."""
1817 _defaultDataset =
'ref'
1823 from dustmaps.sfd
import SFDQuery
1829 coords = SkyCoord(df[
'coord_ra'].values * u.rad, df[
'coord_dec'].values * u.rad)
1830 ebv = self.
sfd(coords)
1833 return pd.Series(ebv, index=df.index).astype(
'float64')
std::vector< SchemaItem< Flag > > * items
Point in an unspecified spherical coordinate system.
__init__(self, col, filt2, filt1, **kwargs)
multilevelColumns(self, parq, **kwargs)
__init__(self, col, **kwargs)
multilevelColumns(self, data, **kwargs)
__call__(self, data, **kwargs)
from_file(cls, filename, **kwargs)
renameCol(cls, col, renameRules)
from_yaml(cls, translationDefinition, **kwargs)
__init__(self, funcs, **kwargs)
pixelScaleArcseconds(self, cd11, cd12, cd21, cd22)
__init__(self, col, colCD_1_1, colCD_1_2, colCD_2_1, colCD_2_2, **kwargs)
__init__(self, col, colCD_1_1, colCD_1_2, colCD_2_1, colCD_2_2, **kwargs)
__init__(self, col, **kwargs)
__init__(self, expr, **kwargs)
__call__(self, catalog, **kwargs)
__init__(self, colXX, colXY, colYY, **kwargs)
__init__(self, colXX, colXY, colYY, **kwargs)
_func(self, df, dropna=True)
_get_columnIndex(self, data)
multilevelColumns(self, data, columnIndex=None, returnTuple=False)
__call__(self, data, dropna=False)
_get_data_columnLevels(self, data, columnIndex=None)
_colsFromDict(self, colDict, columnIndex=None)
difference(self, data1, data2, **kwargs)
_get_data_columnLevelNames(self, data, columnIndex=None)
__init__(self, filt=None, dataset=None, noDup=None)
__init__(self, ra, dec, **kwargs)
__init__(self, instFluxPosCol, instFluxNegCol, instFluxPosErrCol, instFluxNegErrCol, photoCalibCol, photoCalibErrCol, **kwargs)
instFluxToNanojansky(self, instFlux, localCalib)
instFluxErrToNanojanskyErr(self, instFlux, instFluxErr, localCalib, localCalibErr)
instFluxToMagnitude(self, instFlux, localCalib)
__init__(self, instFluxCol, instFluxErrCol, photoCalibCol, photoCalibErrCol, **kwargs)
instFluxErrToMagnitudeErr(self, instFlux, instFluxErr, localCalib, localCalibErr)
computeSkySeparation(self, ra1, dec1, ra2, dec2)
__init__(self, colCD_1_1, colCD_1_2, colCD_2_1, colCD_2_2, **kwargs)
computeDeltaRaDec(self, x, y, cd11, cd12, cd21, cd22)
getSkySeparationFromPixel(self, x1, y1, x2, y2, cd11, cd12, cd21, cd22)
__init__(self, col1, col2, **kwargs)
__init__(self, *args, **kwargs)
__init__(self, col, **kwargs)
dn2flux(self, dn, fluxMag0)
__init__(self, colFlux, colFluxErr=None, **kwargs)
dn2mag(self, dn, fluxMag0)
dn2MagErr(self, dn, dnErr, fluxMag0, fluxMag0Err)
dn2fluxErr(self, dn, dnErr, fluxMag0, fluxMag0Err)
__call__(self, catalog, **kwargs)
__init__(self, colXX, colXY, colYY, **kwargs)
pd.Series _func(self, pd.DataFrame df)
HtmPixelization provides HTM indexing of points and regions.
daf::base::PropertySet * set
init_fromDict(initDict, basePath='lsst.pipe.tasks.functors', typeKey='functor', name=None)
mag_aware_eval(df, expr, log)