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
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:
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 """Compute the level 20 HtmIndex for the catalog.
716 This functor was implemented to satisfy requirements of old APDB interface
717 which required the ``pixelId`` column in DiaObject
with HTM20 index.
718 The APDB interface had migrated to
not need that information, but we keep
719 this
class in case it may be useful for something else.
734 def computePixel(row):
743 return self.
pixelator.index(sphPoint.getVector())
745 return df.apply(computePixel, axis=1, result_type=
'reduce').astype(
'int64')
749 """Append _instFlux to the column name if it doesn't have it already."""
750 if not col.endswith(
'_instFlux'):
756 """Append _instFluxErr to the column name if it doesn't have it already."""
757 if not col.endswith(
'_instFluxErr'):
758 col +=
'_instFluxErr'
763 """Compute calibrated magnitude.
765 Returns the flux at mag=0.
766 The default ``fluxMag0`` is 63095734448.0194, which
is default
for HSC.
767 TO DO: This default should be made configurable
in DM-21955.
769 This calculation hides warnings about invalid values
and dividing by zero.
771 As
with all functors, a ``dataset``
and ``filt`` kwarg should be provided
773 Unlike the default `Functor`, however, the default dataset
for a `Mag`
is
774 ``
'meas'``, rather than ``
'ref'``.
779 Name of flux column
from which to compute magnitude.
780 Can be parseable by the `~lsst.pipe.tasks.functors.fluxName` function;
781 that
is, you can
pass ``
'modelfit_CModel'`` instead of
782 ``
'modelfit_CModel_instFlux'``,
and it will understand.
784 _defaultDataset = 'meas'
789 if calib
is not None:
792 "The 'calib' argument is deprecated, and will be removed after v26.",
808 with warnings.catch_warnings():
809 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
810 warnings.filterwarnings(
'ignore',
r'divide by zero')
815 return f
'mag_{self.col}'
819 """Compute calibrated magnitude uncertainty.
824 Name of the flux column.
829 if self.
calib is not None:
833 "The 'calib' argument is deprecated, and will be removed after v26.",
846 with warnings.catch_warnings():
847 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
848 warnings.filterwarnings(
'ignore',
r'divide by zero')
850 x = df[fluxErrCol] / df[fluxCol]
852 magErr = (2.5 / np.log(10.)) * np.sqrt(x*x + y*y)
857 return super().name +
'_err'
861 """Functor to calculate magnitude difference."""
862 _defaultDataset =
'meas'
874 with warnings.catch_warnings():
875 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
876 warnings.filterwarnings(
'ignore',
r'divide by zero')
877 return -2.5*np.log10(df[self.
col1]/df[self.
col2])
881 return f
'(mag_{self.col1} - mag_{self.col2})'
885 return f
'magDiff_{self.col1}_{self.col2}'
889 """Compute the color between two filters.
891 Computes color by initializing two different `Mag` functors based on the
892 ``col`` and filters provided,
and then returning the difference.
894 This
is enabled by the `_func` method expecting a DataFrame
with a
895 multilevel column index,
with both ``
'band'``
and ``
'column'``, instead of
896 just ``
'column'``, which
is the `Functor` default.
897 This
is controlled by the `_dfLevels` attribute.
899 Also of note, the default dataset
for `Color`
is ``forced_src
'``, whereas
900 for `Mag` it
is ``
'meas'``.
905 Name of the flux column
from which to compute; same
as would be passed
909 Filters
from which to compute magnitude difference.
910 Color computed
is ``
Mag(filt2) -
Mag(filt1)``.
912 _defaultDataset = 'forced_src'
913 _dfLevels = (
'band',
'column')
919 raise RuntimeError(
"Cannot compute Color for %s: %s - %s " % (col, filt2, filt1))
937 mag2 = self.mag2.
_func(df[self.filt2])
938 mag1 = self.mag1.
_func(df[self.filt1])
943 return [self.
mag1.col, self.
mag2.col]
950 return f
'{self.filt2} - {self.filt1} ({self.col})'
954 return f
"{self.col}_{self.filt2.replace('-', '')}m{self.filt1.replace('-', '')}"
958 """This functor subtracts the trace of the PSF second moments from the
959 trace of the second moments of the source.
961 If the HsmShapeAlgorithm measurement is valid, then these will be used
for
963 Otherwise, the SdssShapeAlgorithm measurements will be used.
965 name = 'Deconvolved Moments'
966 shortname =
'deconvolvedMoments'
967 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
968 "ext_shapeHSM_HsmSourceMoments_yy",
969 "base_SdssShape_xx",
"base_SdssShape_yy",
970 "ext_shapeHSM_HsmPsfMoments_xx",
971 "ext_shapeHSM_HsmPsfMoments_yy")
974 """Calculate deconvolved moments."""
975 if "ext_shapeHSM_HsmSourceMoments_xx" in df.columns:
976 hsm = df[
"ext_shapeHSM_HsmSourceMoments_xx"] + df[
"ext_shapeHSM_HsmSourceMoments_yy"]
978 hsm = np.ones(len(df))*np.nan
979 sdss = df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]
980 if "ext_shapeHSM_HsmPsfMoments_xx" in df.columns:
981 psf = df[
"ext_shapeHSM_HsmPsfMoments_xx"] + df[
"ext_shapeHSM_HsmPsfMoments_yy"]
986 raise RuntimeError(
'No psf shape parameter found in catalog')
988 return hsm.where(np.isfinite(hsm), sdss) - psf
992 """Functor to calculate the SDSS trace radius size for sources.
994 The SDSS trace radius size is a measure of size equal to the square root of
995 half of the trace of the second moments tensor measured
with the
996 SdssShapeAlgorithm plugin.
997 This has units of pixels.
999 name = "SDSS Trace Size"
1000 shortname =
'sdssTrace'
1001 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy")
1004 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1009 """Functor to calculate the SDSS trace radius size difference (%) between
1010 the object and the PSF model.
1016 name = "PSF - SDSS Trace Size"
1017 shortname =
'psf_sdssTrace'
1018 _columns = (
"base_SdssShape_xx",
"base_SdssShape_yy",
1019 "base_SdssShape_psf_xx",
"base_SdssShape_psf_yy")
1022 srcSize = np.sqrt(0.5*(df[
"base_SdssShape_xx"] + df[
"base_SdssShape_yy"]))
1023 psfSize = np.sqrt(0.5*(df[
"base_SdssShape_psf_xx"] + df[
"base_SdssShape_psf_yy"]))
1024 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1029 """Functor to calculate the HSM trace radius size for sources.
1031 The HSM trace radius size is a measure of size equal to the square root of
1032 half of the trace of the second moments tensor measured
with the
1033 HsmShapeAlgorithm plugin.
1034 This has units of pixels.
1036 name = 'HSM Trace Size'
1037 shortname =
'hsmTrace'
1038 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1039 "ext_shapeHSM_HsmSourceMoments_yy")
1042 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1043 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1048 """Functor to calculate the HSM trace radius size difference (%) between
1049 the object and the PSF model.
1055 name = 'PSF - HSM Trace Size'
1056 shortname =
'psf_HsmTrace'
1057 _columns = (
"ext_shapeHSM_HsmSourceMoments_xx",
1058 "ext_shapeHSM_HsmSourceMoments_yy",
1059 "ext_shapeHSM_HsmPsfMoments_xx",
1060 "ext_shapeHSM_HsmPsfMoments_yy")
1063 srcSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmSourceMoments_xx"]
1064 + df[
"ext_shapeHSM_HsmSourceMoments_yy"]))
1065 psfSize = np.sqrt(0.5*(df[
"ext_shapeHSM_HsmPsfMoments_xx"]
1066 + df[
"ext_shapeHSM_HsmPsfMoments_yy"]))
1067 sizeDiff = 100*(srcSize - psfSize)/(0.5*(srcSize + psfSize))
1072 """Functor to calculate the PSF FWHM with second moments measured from the
1073 HsmShapeAlgorithm plugin.
1075 This is in units of arcseconds,
and assumes the hsc_rings_v1 skymap pixel
1076 scale of 0.168 arcseconds/pixel.
1080 This conversion assumes the PSF
is Gaussian, which
is not always the case.
1082 name = 'HSM Psf FWHM'
1083 _columns = (
'ext_shapeHSM_HsmPsfMoments_xx',
'ext_shapeHSM_HsmPsfMoments_yy')
1086 SIGMA2FWHM = 2*np.sqrt(2*np.log(2))
1090 0.5*(df[
'ext_shapeHSM_HsmPsfMoments_xx'] + df[
'ext_shapeHSM_HsmPsfMoments_yy']))
1094 r"""Calculate :math:`e_1` ellipticity component for sources, defined as:
1097 e_1 &= (I_{xx}-I_{yy})/(I_{xx}+I_{yy})
1103 name = "Distortion Ellipticity (e1)"
1104 shortname =
"Distortion"
1122 r"""Calculate :math:`e_2` ellipticity component for sources, defined as:
1125 e_2 &= 2I_{xy}/(I_{xx}+I_{yy})
1131 name = "Ellipticity e2"
1148 """Calculate the radius from the quadrupole moments.
1150 This returns the fourth root of the determinant of the second moments
1151 tensor, which has units of pixels.
1174 """Computations using the stored localWcs."""
1175 name =
"LocalWcsOperations"
1190 """Compute the distance on the sphere from x2, y1 to x1, y1.
1194 x : `~pandas.Series`
1196 y : `~pandas.Series`
1198 cd11 : `~pandas.Series`
1199 [1, 1] element of the local Wcs affine transform.
1200 cd11 : `~pandas.Series`
1201 [1, 1] element of the local Wcs affine transform.
1202 cd12 : `~pandas.Series`
1203 [1, 2] element of the local Wcs affine transform.
1204 cd21 : `~pandas.Series`
1205 [2, 1] element of the local Wcs affine transform.
1206 cd22 : `~pandas.Series`
1207 [2, 2] element of the local Wcs affine transform.
1212 RA and dec conversion of x
and y given the local Wcs.
1213 Returned units are
in radians.
1216 return (x * cd11 + y * cd12, x * cd21 + y * cd22)
1219 """Compute the local pixel scale conversion.
1223 ra1 : `~pandas.Series`
1224 Ra of the first coordinate in radians.
1225 dec1 : `~pandas.Series`
1226 Dec of the first coordinate
in radians.
1227 ra2 : `~pandas.Series`
1228 Ra of the second coordinate
in radians.
1229 dec2 : `~pandas.Series`
1230 Dec of the second coordinate
in radians.
1234 dist : `~pandas.Series`
1235 Distance on the sphere
in radians.
1237 deltaDec = dec2 - dec1
1239 return 2 * np.arcsin(
1241 np.sin(deltaDec / 2) ** 2
1242 + np.cos(dec2) * np.cos(dec1) * np.sin(deltaRa / 2) ** 2))
1245 """Compute the distance on the sphere from x2, y1 to x1, y1.
1249 x1 : `~pandas.Series`
1251 y1 : `~pandas.Series`
1253 x2 : `~pandas.Series`
1255 y2 : `~pandas.Series`
1257 cd11 : `~pandas.Series`
1258 [1, 1] element of the local Wcs affine transform.
1259 cd11 : `~pandas.Series`
1260 [1, 1] element of the local Wcs affine transform.
1261 cd12 : `~pandas.Series`
1262 [1, 2] element of the local Wcs affine transform.
1263 cd21 : `~pandas.Series`
1264 [2, 1] element of the local Wcs affine transform.
1265 cd22 : `~pandas.Series`
1266 [2, 2] element of the local Wcs affine transform.
1270 Distance : `~pandas.Series`
1271 Arcseconds per pixel at the location of the local WC.
1280 """Compute the local pixel scale from the stored CDMatrix.
1292 """Compute the local pixel to scale conversion in arcseconds.
1296 cd11 : `~pandas.Series`
1297 [1, 1] element of the local Wcs affine transform in radians.
1298 cd11 : `~pandas.Series`
1299 [1, 1] element of the local Wcs affine transform
in radians.
1300 cd12 : `~pandas.Series`
1301 [1, 2] element of the local Wcs affine transform
in radians.
1302 cd21 : `~pandas.Series`
1303 [2, 1] element of the local Wcs affine transform
in radians.
1304 cd22 : `~pandas.Series`
1305 [2, 2] element of the local Wcs affine transform
in radians.
1309 pixScale : `~pandas.Series`
1310 Arcseconds per pixel at the location of the local WC.
1312 return 3600 * np.degrees(np.sqrt(np.fabs(cd11 * cd22 - cd12 * cd21)))
1322 """Convert a value in units of pixels to units of arcseconds."""
1340 return f
"{self.col}_asArcseconds"
1358 """Convert a value in units of pixels squared to units of arcseconds
1378 return f
"{self.col}_asArcsecondsSq"
1393 return df[self.
col] * pixScale * pixScale
1397 """Return the band used to seed multiband forced photometry.
1399 This functor is to be used on Object tables.
1400 It converts the boolean merge_measurements_{band} columns into a single
1401 string representing the first band
for which merge_measurements_{band}
1404 Assumes the default priority order of i, r, z, y, g, u.
1406 name = 'Reference Band'
1407 shortname =
'refBand'
1411 return [
"merge_measurement_i",
1412 "merge_measurement_r",
1413 "merge_measurement_z",
1414 "merge_measurement_y",
1415 "merge_measurement_g",
1416 "merge_measurement_u"]
1418 def _func(self, df: pd.DataFrame) -> pd.Series:
1419 def getFilterAliasName(row):
1421 colName = row.idxmax()
1422 return colName.replace(
'merge_measurement_',
'')
1426 columns = [col
for col
in self.
columnscolumns if col
in df.columns]
1428 return df[columns].apply(getFilterAliasName, axis=1,
1429 result_type=
'reduce').astype(
'object')
1433 """Base class for Object table calibrated fluxes and magnitudes."""
1435 AB_FLUX_SCALE = (0 * u.ABmag).to_value(u.nJy)
1436 LOG_AB_FLUX_SCALE = 12.56
1437 FIVE_OVER_2LOG10 = 1.085736204758129569
1441 def __init__(self, colFlux, colFluxErr=None, calib=None, **kwargs):
1447 if calib
is not None:
1461 return f
'mag_{self.col}'
1465 """Compute sqrt(a^2 + b^2) without under/overflow."""
1466 if np.abs(a) < np.abs(b):
1471 return np.abs(a) * np.sqrt(1. + q*q)
1474 """Convert instrumental flux to nanojanskys."""
1478 """Convert instrumental flux to AB magnitude."""
1479 with warnings.catch_warnings():
1480 warnings.filterwarnings(
'ignore',
r'invalid value encountered')
1481 warnings.filterwarnings(
'ignore',
r'divide by zero')
1482 return -2.5 * np.log10(dn/fluxMag0)
1485 """Convert instrumental flux error to nanojanskys."""
1486 retVal = self.
vhypot(dn * fluxMag0Err, dnErr * fluxMag0)
1491 """Convert instrumental flux error to AB magnitude error."""
1492 retVal = self.
dn2fluxErr(dn, dnErr, fluxMag0, fluxMag0Err) / self.
dn2flux(dn, fluxMag0)
1497 """Convert instrumental flux to nanojanskys."""
1503 """Convert instrumental flux error to nanojanskys."""
1510 return pd.Series(retArr, index=df.index)
1514 """Base class for calibrating the specified instrument flux column using
1515 the local photometric calibration.
1520 Name of the instrument flux column.
1521 instFluxErrCol : `str`
1522 Name of the assocated error columns for ``instFluxCol``.
1523 photoCalibCol : `str`
1524 Name of local calibration column.
1525 photoCalibErrCol : `str`
1526 Error associated
with ``photoCalibCol``
1533 logNJanskyToAB = (1 * u.nJy).to_value(u.ABmag)
1548 """Convert instrument flux to nanojanskys.
1552 instFlux : `~numpy.ndarray` or `~pandas.Series`
1553 Array of instrument flux measurements.
1554 localCalib : `~numpy.ndarray`
or `~pandas.Series`
1555 Array of local photometric calibration estimates.
1559 calibFlux : `~numpy.ndarray`
or `~pandas.Series`
1560 Array of calibrated flux measurements.
1562 return instFlux * localCalib
1565 """Convert instrument flux to nanojanskys.
1569 instFlux : `~numpy.ndarray` or `~pandas.Series`
1570 Array of instrument flux measurements.
1571 instFluxErr : `~numpy.ndarray`
or `~pandas.Series`
1572 Errors on associated ``instFlux`` values.
1573 localCalib : `~numpy.ndarray`
or `~pandas.Series`
1574 Array of local photometric calibration estimates.
1575 localCalibErr : `~numpy.ndarray`
or `~pandas.Series`
1576 Errors on associated ``localCalib`` values.
1580 calibFluxErr : `~numpy.ndarray`
or `~pandas.Series`
1581 Errors on calibrated flux measurements.
1583 return np.hypot(instFluxErr * localCalib, instFlux * localCalibErr)
1586 """Convert instrument flux to nanojanskys.
1590 instFlux : `~numpy.ndarray` or `~pandas.Series`
1591 Array of instrument flux measurements.
1592 localCalib : `~numpy.ndarray`
or `~pandas.Series`
1593 Array of local photometric calibration estimates.
1597 calibMag : `~numpy.ndarray`
or `~pandas.Series`
1598 Array of calibrated AB magnitudes.
1603 """Convert instrument flux err to nanojanskys.
1607 instFlux : `~numpy.ndarray` or `~pandas.Series`
1608 Array of instrument flux measurements.
1609 instFluxErr : `~numpy.ndarray`
or `~pandas.Series`
1610 Errors on associated ``instFlux`` values.
1611 localCalib : `~numpy.ndarray`
or `~pandas.Series`
1612 Array of local photometric calibration estimates.
1613 localCalibErr : `~numpy.ndarray`
or `~pandas.Series`
1614 Errors on associated ``localCalib`` values.
1618 calibMagErr: `~numpy.ndarray`
or `~pandas.Series`
1619 Error on calibrated AB magnitudes.
1626 """Compute calibrated fluxes using the local calibration value.
1628 This returns units of nanojanskys.
1637 return f
'flux_{self.instFluxCol}'
1644 """Compute calibrated flux errors using the local calibration value.
1646 This returns units of nanojanskys.
1656 return f
'fluxErr_{self.instFluxCol}'
1664 """Compute absolute mean of dipole fluxes.
1670 LocalDipoleMeanFluxErr
1672 LocalDipoleDiffFluxErr
1702 return f
'dipMeanFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1710 """Compute the error on the absolute mean of dipole fluxes.
1718 LocalDipoleDiffFluxErr
1732 return f
'dipMeanFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1743 """Compute the absolute difference of dipole fluxes.
1745 Calculated value is (abs(pos) - abs(neg)).
1752 LocalDipoleMeanFluxErr
1753 LocalDipoleDiffFluxErr
1764 return f
'dipDiffFlux_{self.instFluxPosCol}_{self.instFluxNegCol}'
1772 """Compute the error on the absolute difference of dipole fluxes.
1779 LocalDipoleMeanFluxErr
1794 return f
'dipDiffFluxErr_{self.instFluxPosCol}_{self.instFluxNegCol}'
1805 """Compute E(B-V) from dustmaps.sfd."""
1806 _defaultDataset =
'ref'
1812 from dustmaps.sfd
import SFDQuery
1818 coords = SkyCoord(df[
'coord_ra'].values * u.rad, df[
'coord_dec'].values * u.rad)
1819 ebv = self.
sfd(coords)
1822 return pd.Series(ebv, index=df.index).astype(
'float64')
std::vector< SchemaItem< Flag > > * items
table::Key< std::string > object
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, calib=None, **kwargs)
dn2flux(self, dn, fluxMag0)
dn2mag(self, dn, fluxMag0)
dn2MagErr(self, dn, dnErr, fluxMag0, fluxMag0Err)
__init__(self, colFlux, colFluxErr=None, calib=None, **kwargs)
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::PropertyList * list
daf::base::PropertySet * set
init_fromDict(initDict, basePath='lsst.pipe.tasks.functors', typeKey='functor', name=None)
mag_aware_eval(df, expr, log)