22"""Plugins for use in DiaSource summary statistics.
25as defined in the schema of the Apdb both in name and units.
31from astropy.stats
import median_absolute_deviation
34from scipy.optimize
import lsq_linear
40from astropy.timeseries
import LombScargle
41from astropy.timeseries
import LombScargleMultiband
45from .diaCalculation
import (
46 DiaObjectCalculationPluginConfig,
47 DiaObjectCalculationPlugin)
48from .pluginRegistry
import register
51__all__ = (
"MeanDiaPositionConfig",
"MeanDiaPosition",
52 "HTMIndexDiaPosition",
"HTMIndexDiaPositionConfig",
53 "NumDiaSourcesDiaPlugin",
"NumDiaSourcesDiaPluginConfig",
54 "SimpleSourceFlagDiaPlugin",
"SimpleSourceFlagDiaPluginConfig",
55 "WeightedMeanDiaPsfFluxConfig",
"WeightedMeanDiaPsfFlux",
56 "PercentileDiaPsfFlux",
"PercentileDiaPsfFluxConfig",
57 "SigmaDiaPsfFlux",
"SigmaDiaPsfFluxConfig",
58 "Chi2DiaPsfFlux",
"Chi2DiaPsfFluxConfig",
59 "MadDiaPsfFlux",
"MadDiaPsfFluxConfig",
60 "SkewDiaPsfFlux",
"SkewDiaPsfFluxConfig",
61 "MinMaxDiaPsfFlux",
"MinMaxDiaPsfFluxConfig",
62 "MaxSlopeDiaPsfFlux",
"MaxSlopeDiaPsfFluxConfig",
63 "ErrMeanDiaPsfFlux",
"ErrMeanDiaPsfFluxConfig",
64 "LinearFitDiaPsfFlux",
"LinearFitDiaPsfFluxConfig",
65 "StetsonJDiaPsfFlux",
"StetsonJDiaPsfFluxConfig",
66 "WeightedMeanDiaTotFlux",
"WeightedMeanDiaTotFluxConfig",
67 "SigmaDiaTotFlux",
"SigmaDiaTotFluxConfig",
68 "LombScarglePeriodogram",
"LombScarglePeriodogramConfig",
69 "LombScarglePeriodogramMulti",
"LombScarglePeriodogramMultiConfig",
70 "UnphysicalDiaSourceSeparation")
74 """Decorator for generically catching numpy warnings.
76 def decoratorCatchWarnings(func):
77 @functools.wraps(func)
78 def wrapperCatchWarnings(*args, **kwargs):
79 with warnings.catch_warnings():
81 warnings.filterwarnings(
"ignore", val)
82 return func(*args, **kwargs)
83 return wrapperCatchWarnings
86 return decoratorCatchWarnings
88 return decoratorCatchWarnings(_func)
95 default_dtype=np.float64,
98 force_int_to_float=False,
101 Assign from a source dataframe to a target dataframe in a type safe way.
105 target : `pd.DataFrame`
106 Target pandas dataframe.
107 source : `pd.DataFrame` or `pd.Series`
108 Grouped source dataframe.
109 columns : `list` [`str`]
110 List of columns to transfer.
111 default_dtype : `np.dtype`, optional
112 Default datatype (if not in target).
113 int_fill_value : `int`, optional
114 Fill value for integer columns to avoid pandas insisting
115 that everything should be float-ified as nans.
116 force_int_to_float : `bool`, optional
117 Force integer columns to float columns? Use this option
118 for backwards compatibility for old pandas misfeatures which
119 are expected by some downstream processes.
121 is_series = isinstance(source, pd.Series)
126 source_col = source[col]
128 matched_length =
False
129 if col
in target.columns:
130 target_dtype = target[col].dtype
131 matched_length = len(target) == len(source)
133 target_dtype = default_dtype
135 if (matched_length
or pd.api.types.is_float_dtype(target_dtype))
and not force_int_to_float:
138 target.loc[:, col] = source_col.astype(target_dtype)
144 target[col] = target[col].astype(np.float64)
146 target.loc[:, col] = source_col.astype(np.float64)
147 if not force_int_to_float:
149 target[col] = target[col].fillna(int_fill_value).astype(target_dtype)
154 Computes an optimized periodogram frequency grid for a given time series.
160 oversampling_factor : `int`, optional
161 The oversampling factor for frequency grid.
162 nyquist_factor : `int`, optional
163 The Nyquist factor for frequency grid.
167 frequencies : `array`
168 The computed optimized periodogram frequency grid.
172 baseline = np.max(x0) - np.min(x0)
175 frequency_resolution = 1. / baseline / oversampling_factor
177 num_frequencies = int(
178 0.5 * oversampling_factor * nyquist_factor * num_points)
179 frequencies = frequency_resolution + \
180 frequency_resolution * np.arange(num_frequencies)
189@
register(
"ap_lombScarglePeriodogram")
191 """Compute the single-band period of a DiaObject given a set of DiaSources.
193 ConfigClass = LombScarglePeriodogramConfig
196 outputCols = [
"period",
"power"]
203 @catchWarnings(warns=["All-NaN slice encountered"])
209 """Compute the periodogram.
213 diaObjects : `pandas.DataFrame`
214 Summary objects to store values in.
215 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
216 Catalog of DiaSources summarized by this DiaObject.
220 if (periodCol := f
"{band}_period")
not in diaObjects.columns:
221 diaObjects[periodCol] = np.nan
222 if (powerCol := f
"{band}_power")
not in diaObjects.columns:
223 diaObjects[powerCol] = np.nan
225 def _calculate_period(df, min_detections=5, nterms=1, oversampling_factor=5, nyquist_factor=100):
226 """Compute the Lomb-Scargle periodogram given a set of DiaSources.
230 df : `pandas.DataFrame`
232 min_detections : `int`, optional
233 The minimum number of detections.
234 nterms : `int`, optional
235 The number of terms in the Lomb-Scargle model.
236 oversampling_factor : `int`, optional
237 The oversampling factor for frequency grid.
238 nyquist_factor : `int`, optional
239 The Nyquist factor for frequency grid.
243 pd_tab : `pandas.Series`
244 The output DataFrame with the Lomb-Scargle parameters.
246 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
247 np.isnan(df[
"midpointMjdTai"]))]
249 if len(tmpDf) < min_detections:
250 return pd.Series({periodCol: np.nan, powerCol: np.nan})
252 time = tmpDf[
"midpointMjdTai"].to_numpy()
253 flux = tmpDf[
"psfFlux"].to_numpy()
254 flux_err = tmpDf[
"psfFluxErr"].to_numpy()
256 lsp = LombScargle(time, flux, dy=flux_err, nterms=nterms)
258 time, oversampling_factor=oversampling_factor, nyquist_factor=nyquist_factor)
260 power = lsp.power(f_grid)
262 return pd.Series({periodCol: period[np.argmax(power)],
263 powerCol: np.max(power)})
265 diaObjects.loc[:, [periodCol, powerCol]
266 ] = filterDiaSources.apply(_calculate_period)
273@
register(
"ap_lombScarglePeriodogramMulti")
275 """Compute the multi-band LombScargle periodogram of a DiaObject given a set of DiaSources.
277 ConfigClass = LombScarglePeriodogramMultiConfig
280 outputCols = [
"multiPeriod",
"multiPower",
281 "multiFap",
"multiAmp",
"multiPhase"]
290 """Calculate the False-Alarm probability using the Baluev approximation.
297 The number of detections.
299 The maximum period in the grid.
301 The maximum power in the grid.
305 fap_estimate : `float`
306 The False-Alarm probability Baluev approximation.
310 .. [1] Baluev, R. V. 2008, MNRAS, 385, 1279
311 .. [2] Süveges, M., Guy, L.P., Eyer, L., et al. 2015, MNRAS, 450, 2052
316 gam_ratio = math.factorial(int((n - 1)/2)) / math.factorial(int((n - 2)/2))
318 return gam_ratio * np.sqrt(
319 4*np.pi*statistics.variance(time)
320 ) * fu * (1-zmax)**((n-4)/2) * np.sqrt(zmax)
324 """Generate the Lomb-Scargle parameters.
327 lsp_model : `astropy.timeseries.LombScargleMultiband`
328 The Lomb-Scargle model.
332 The bands of the time series.
337 The amplitude of the time series.
339 The phase of the time series.
343 .. [1] VanderPlas, J. T., & Ivezic, Z. 2015, ApJ, 812, 18
345 best_params = lsp_model.model_parameters(fbest, units=
True)
347 name_params = [f
"theta_base_{i}" for i
in range(3)]
348 name_params += [f
"theta_band_{band}_{i}" for band
in np.unique(bands)
for i
in range(3)]
350 df_params = pd.DataFrame([best_params], columns=name_params)
352 unique_bands = np.unique(bands)
354 amplitude_band = [np.sqrt(df_params[f
"theta_band_{band}_1"]**2
355 + df_params[f
"theta_band_{band}_2"]**2)
356 for band
in unique_bands]
357 phase_bands = [np.arctan2(df_params[f
"theta_band_{band}_2"],
358 df_params[f
"theta_band_{band}_1"])
for band
in unique_bands]
360 amp = [a[0]
for a
in amplitude_band]
361 ph = [p[0]
for p
in phase_bands]
365 @catchWarnings(warns=["All-NaN slice encountered"])
370 """Compute the multi-band LombScargle periodogram of a DiaObject given
375 diaObjects : `pandas.DataFrame`
376 Summary objects to store values in.
377 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
378 Catalog of DiaSources summarized by this DiaObject.
380 Unused kwargs that are always passed to a plugin.
383 bands_arr = diaSources[
'band'].unique().values
384 unique_bands = np.unique(np.concatenate(bands_arr))
386 if (periodCol :=
"multiPeriod")
not in diaObjects.columns:
387 diaObjects[periodCol] = np.nan
388 if (powerCol :=
"multiPower")
not in diaObjects.columns:
389 diaObjects[powerCol] = np.nan
390 if (fapCol :=
"multiFap")
not in diaObjects.columns:
391 diaObjects[fapCol] = np.nan
393 phaseCol =
"multiPhase"
394 for i
in range(len(unique_bands)):
395 ampCol_band = f
"{unique_bands[i]}_{ampCol}"
396 if ampCol_band
not in diaObjects.columns:
397 diaObjects[ampCol_band] = np.nan
398 phaseCol_band = f
"{unique_bands[i]}_{phaseCol}"
399 if phaseCol_band
not in diaObjects.columns:
400 diaObjects[phaseCol_band] = np.nan
402 def _calculate_period_multi(df, all_unique_bands,
403 min_detections=9, oversampling_factor=5, nyquist_factor=100):
404 """Calculate the multi-band Lomb-Scargle periodogram.
408 df : `pandas.DataFrame`
410 all_unique_bands : `list` of `str`
411 List of all bands present in the diaSource table that is being worked on.
412 min_detections : `int`, optional
413 The minimum number of detections, including all bands.
414 oversampling_factor : `int`, optional
415 The oversampling factor for frequency grid.
416 nyquist_factor : `int`, optional
417 The Nyquist factor for frequency grid.
421 pd_tab : `pandas.Series`
422 The output DataFrame with the Lomb-Scargle parameters.
424 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
425 np.isnan(df[
"midpointMjdTai"]))]
427 if (len(tmpDf)) < min_detections:
428 pd_tab_nodet = pd.Series({periodCol: np.nan,
431 for band
in all_unique_bands:
432 pd_tab_nodet[f
"{band}_{ampCol}"] = np.nan
433 pd_tab_nodet[f
"{band}_{phaseCol}"] = np.nan
437 time = tmpDf[
"midpointMjdTai"].to_numpy()
438 flux = tmpDf[
"psfFlux"].to_numpy()
439 flux_err = tmpDf[
"psfFluxErr"].to_numpy()
440 bands = tmpDf[
"band"].to_numpy()
442 lsp = LombScargleMultiband(time, flux, bands, dy=flux_err,
443 nterms_base=1, nterms_band=1)
446 time, oversampling_factor=oversampling_factor, nyquist_factor=nyquist_factor)
448 power = lsp.power(f_grid)
451 time, len(time), period[np.argmax(power)], np.max(power))
455 pd_tab = pd.Series({periodCol: period[np.argmax(power)],
456 powerCol: np.max(power),
461 for band
in all_unique_bands:
462 pd_tab[f
"{band}_{ampCol}"] = np.nan
463 pd_tab[f
"{band}_{phaseCol}"] = np.nan
466 unique_bands = np.unique(bands)
467 for i
in range(len(unique_bands)):
468 pd_tab[f
"{unique_bands[i]}_{ampCol}"] = params_table_new[0][i]
469 pd_tab[f
"{unique_bands[i]}_{phaseCol}"] = params_table_new[1][i]
473 columns_list = [periodCol, powerCol, fapCol]
474 for i
in range(len(unique_bands)):
475 columns_list.append(f
"{unique_bands[i]}_{ampCol}")
476 columns_list.append(f
"{unique_bands[i]}_{phaseCol}")
478 diaObjects.loc[:, columns_list
479 ] = diaSources.apply(_calculate_period_multi, unique_bands)
483 """Raised if associated DiaSources are unphysically separated.
488 Observed separation in arseconds.
489 max_allowed_separation : `float`
490 Configured maximum separation in arcseconds.
493 def __init__(self, separation, max_allowed_separation) -> None:
496 super().
__init__(f
"Observed DiaSource separation {separation} exceeds allowed value of "
497 f
"{max_allowed_separation}")
508 MaxAllowedDiaSourceSeparation = pexConfig.Field(
511 doc=
"Max allowed separation of associated DiaSources in arcsec. "
512 "Raises if unphysical separation is found. "
516@register("ap_meanPosition")
518 """Compute the mean position of a DiaObject given a set of DiaSources.
521 ConfigClass = MeanDiaPositionConfig
525 outputCols = [
"ra",
"dec"]
533 """Compute the mean ra/dec position of the diaObject given the
538 diaObjects : `pandas.DataFrame`
539 Summary objects to store values in.
540 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
541 Catalog of DiaSources summarized by this DiaObject.
543 Any additional keyword arguments that may be passed to the plugin.
546 if outCol
not in diaObjects.columns:
547 diaObjects[outCol] = np.nan
549 def _computeMeanPos(df):
551 for idx, src
in df.iterrows())
552 aveCoord = geom.averageSpherePoint(coords)
555 maxSep = max(aveCoord.separation(coord).asArcseconds()
for coord
in coords)
557 if maxSep > self.
config.MaxAllowedDiaSourceSeparation:
559 self.
config.MaxAllowedDiaSourceSeparation)
561 return pd.Series({
"ra": aveCoord.getRa().asDegrees(),
562 "dec": aveCoord.getDec().asDegrees()})
564 ans = diaSources.apply(_computeMeanPos)
570 htmLevel = pexConfig.Field(
572 doc=
"Level of the HTM pixelization.",
577@register("ap_HTMIndex")
579 """Compute the mean position of a DiaObject given a set of DiaSources.
583 This plugin was implemented to satisfy requirements of old APDB interface
584 which required ``pixelId`` column in DiaObject with HTM20 index. APDB
585 interface had migrated to not need that information, but we keep this
586 plugin in case it may be useful for something else.
588 ConfigClass = HTMIndexDiaPositionConfig
592 inputCols = [
"ra",
"dec"]
593 outputCols = [
"pixelId"]
597 DiaObjectCalculationPlugin.__init__(self, config, name, metadata)
605 """Compute the mean position of a DiaObject given a set of DiaSources
609 diaObjects : `pandas.dataFrame`
610 Summary objects to store values in and read ra/dec from.
612 Id of the diaObject to update.
614 Any additional keyword arguments that may be passed to the plugin.
617 diaObjects.at[diaObjectId,
"ra"] * geom.degrees,
618 diaObjects.at[diaObjectId,
"dec"] * geom.degrees)
619 diaObjects.at[diaObjectId,
"pixelId"] = self.
pixelator.index(
620 sphPoint.getVector())
629 """Compute the total number of DiaSources associated with this DiaObject.
632 ConfigClass = NumDiaSourcesDiaPluginConfig
633 outputCols = [
"nDiaSources"]
642 """Compute the total number of DiaSources associated with this DiaObject.
647 Summary object to store values in and read ra/dec from.
649 Any additional keyword arguments that may be passed to the plugin.
660 """Find if any DiaSource is flagged.
662 Set the DiaObject flag if any DiaSource is flagged.
665 ConfigClass = NumDiaSourcesDiaPluginConfig
666 outputCols = [
"flags"]
675 """Find if any DiaSource is flagged.
677 Set the DiaObject flag if any DiaSource is flagged.
682 Summary object to store values in and read ra/dec from.
684 Any additional keyword arguments that may be passed to the plugin.
695 """Compute the weighted mean and mean error on the point source fluxes
696 of the DiaSource measured on the difference image.
698 Additionally store number of usable data points.
701 ConfigClass = WeightedMeanDiaPsfFluxConfig
702 outputCols = [
"psfFluxMean",
"psfFluxMeanErr",
"psfFluxNdata"]
710 @catchWarnings(warns=[
"invalid value encountered",
718 """Compute the weighted mean and mean error of the point source flux.
723 Summary object to store values in.
724 diaSources : `pandas.DataFrame`
725 DataFrame representing all diaSources associated with this
727 filterDiaSources : `pandas.DataFrame`
728 DataFrame representing diaSources associated with this
729 diaObject that are observed in the band pass ``band``.
731 Simple, string name of the filter for the flux being calculated.
733 Any additional keyword arguments that may be passed to the plugin.
735 meanName =
"{}_psfFluxMean".format(band)
736 errName =
"{}_psfFluxMeanErr".format(band)
737 nDataName =
"{}_psfFluxNdata".format(band)
738 if meanName
not in diaObjects.columns:
739 diaObjects[meanName] = np.nan
740 if errName
not in diaObjects.columns:
741 diaObjects[errName] = np.nan
742 if nDataName
not in diaObjects.columns:
743 diaObjects[nDataName] = 0
745 def _weightedMean(df):
746 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
747 np.isnan(df[
"psfFluxErr"]))]
748 tot_weight = np.nansum(1 / tmpDf[
"psfFluxErr"] ** 2)
749 fluxMean = np.nansum(tmpDf[
"psfFlux"]
750 / tmpDf[
"psfFluxErr"] ** 2)
751 fluxMean /= tot_weight
753 fluxMeanErr = np.sqrt(1 / tot_weight)
756 nFluxData = len(tmpDf)
758 return pd.Series({meanName: fluxMean,
759 errName: fluxMeanErr,
760 nDataName: nFluxData},
762 df = filterDiaSources.apply(_weightedMean).astype(diaObjects.dtypes[[meanName, errName, nDataName]])
768 percentiles = pexConfig.ListField(
770 default=[5, 25, 50, 75, 95],
771 doc=
"Percentiles to calculate to compute values for. Should be "
776@register("ap_percentileFlux")
778 """Compute percentiles of diaSource fluxes.
781 ConfigClass = PercentileDiaPsfFluxConfig
787 def __init__(self, config, name, metadata, **kwargs):
788 DiaObjectCalculationPlugin.__init__(self,
793 self.
outputCols = [
"psfFluxPercentile{:02d}".format(percent)
794 for percent
in self.
config.percentiles]
800 @catchWarnings(warns=["All-NaN slice encountered"])
807 """Compute the percentile fluxes of the point source flux.
812 Summary object to store values in.
813 diaSources : `pandas.DataFrame`
814 DataFrame representing all diaSources associated with this
816 filterDiaSources : `pandas.DataFrame`
817 DataFrame representing diaSources associated with this
818 diaObject that are observed in the band pass ``band``.
820 Simple, string name of the filter for the flux being calculated.
822 Any additional keyword arguments that may be passed to the plugin.
825 for tilePercent
in self.
config.percentiles:
826 pTileName =
"{}_psfFluxPercentile{:02d}".format(band,
828 pTileNames.append(pTileName)
829 if pTileName
not in diaObjects.columns:
830 diaObjects[pTileName] = np.nan
832 def _fluxPercentiles(df):
833 pTiles = np.nanpercentile(df[
"psfFlux"], self.
config.percentiles)
835 dict((tileName, pTile)
836 for tileName, pTile
in zip(pTileNames, pTiles)))
840 filterDiaSources.apply(_fluxPercentiles),
851 """Compute scatter of diaSource fluxes.
854 ConfigClass = SigmaDiaPsfFluxConfig
856 outputCols = [
"psfFluxSigma"]
870 """Compute the sigma fluxes of the point source flux.
875 Summary object to store values in.
876 diaSources : `pandas.DataFrame`
877 DataFrame representing all diaSources associated with this
879 filterDiaSources : `pandas.DataFrame`
880 DataFrame representing diaSources associated with this
881 diaObject that are observed in the band pass ``band``.
883 Simple, string name of the filter for the flux being calculated.
885 Any additional keyword arguments that may be passed to the plugin.
889 column =
"{}_psfFluxSigma".format(band)
893 filterDiaSources.psfFlux.std(),
904 """Compute chi2 of diaSource fluxes.
907 ConfigClass = Chi2DiaPsfFluxConfig
910 inputCols = [
"psfFluxMean"]
912 outputCols = [
"psfFluxChi2"]
920 @catchWarnings(warns=["All-NaN slice encountered"])
927 """Compute the chi2 of the point source fluxes.
932 Summary object to store values in.
933 diaSources : `pandas.DataFrame`
934 DataFrame representing all diaSources associated with this
936 filterDiaSources : `pandas.DataFrame`
937 DataFrame representing diaSources associated with this
938 diaObject that are observed in the band pass ``band``.
940 Simple, string name of the filter for the flux being calculated.
942 Any additional keyword arguments that may be passed to the plugin.
944 meanName =
"{}_psfFluxMean".format(band)
945 column =
"{}_psfFluxChi2".format(band)
948 delta = (df[
"psfFlux"]
949 - diaObjects.at[df.diaObjectId.iat[0], meanName])
950 return np.nansum((delta / df[
"psfFluxErr"]) ** 2)
954 filterDiaSources.apply(_chi2),
965 """Compute median absolute deviation of diaSource fluxes.
968 ConfigClass = MadDiaPsfFluxConfig
972 outputCols = [
"psfFluxMAD"]
980 @catchWarnings(warns=["All-NaN slice encountered"])
987 """Compute the median absolute deviation of the point source fluxes.
992 Summary object to store values in.
993 diaSources : `pandas.DataFrame`
994 DataFrame representing all diaSources associated with this
996 filterDiaSources : `pandas.DataFrame`
997 DataFrame representing diaSources associated with this
998 diaObject that are observed in the band pass ``band``.
1000 Simple, string name of the filter for the flux being calculated.
1002 Any additional keyword arguments that may be passed to the plugin.
1004 column =
"{}_psfFluxMAD".format(band)
1008 filterDiaSources.psfFlux.apply(median_absolute_deviation, ignore_nan=
True),
1019 """Compute the skew of diaSource fluxes.
1022 ConfigClass = SkewDiaPsfFluxConfig
1026 outputCols = [
"psfFluxSkew"]
1040 """Compute the skew of the point source fluxes.
1045 Summary object to store values in.
1046 diaSources : `pandas.DataFrame`
1047 DataFrame representing all diaSources associated with this
1049 filterDiaSources : `pandas.DataFrame`
1050 DataFrame representing diaSources associated with this
1051 diaObject that are observed in the band pass ``band``.
1053 Simple, string name of the filter for the flux being calculated.
1055 Any additional keyword arguments that may be passed to the plugin.
1057 column =
"{}_psfFluxSkew".format(band)
1061 filterDiaSources.psfFlux.skew(),
1072 """Compute min/max of diaSource fluxes.
1075 ConfigClass = MinMaxDiaPsfFluxConfig
1079 outputCols = [
"psfFluxMin",
"psfFluxMax"]
1093 """Compute min/max of the point source fluxes.
1098 Summary object to store values in.
1099 diaSources : `pandas.DataFrame`
1100 DataFrame representing all diaSources associated with this
1102 filterDiaSources : `pandas.DataFrame`
1103 DataFrame representing diaSources associated with this
1104 diaObject that are observed in the band pass ``band``.
1106 Simple, string name of the filter for the flux being calculated.
1108 Any additional keyword arguments that may be passed to the plugin.
1110 minName =
"{}_psfFluxMin".format(band)
1111 if minName
not in diaObjects.columns:
1112 diaObjects[minName] = np.nan
1113 maxName =
"{}_psfFluxMax".format(band)
1114 if maxName
not in diaObjects.columns:
1115 diaObjects[maxName] = np.nan
1119 filterDiaSources.psfFlux.min(),
1124 filterDiaSources.psfFlux.max(),
1135 """Compute the maximum ratio time ordered deltaFlux / deltaTime.
1138 ConfigClass = MinMaxDiaPsfFluxConfig
1142 outputCols = [
"psfFluxMaxSlope"]
1156 """Compute the maximum ratio time ordered deltaFlux / deltaTime.
1161 Summary object to store values in.
1162 diaSources : `pandas.DataFrame`
1163 DataFrame representing all diaSources associated with this
1165 filterDiaSources : `pandas.DataFrame`
1166 DataFrame representing diaSources associated with this
1167 diaObject that are observed in the band pass ``band``.
1169 Simple, string name of the filter for the flux being calculated.
1171 Any additional keyword arguments that may be passed to the plugin.
1175 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
1176 np.isnan(df[
"midpointMjdTai"]))]
1179 times = tmpDf[
"midpointMjdTai"].to_numpy()
1180 timeArgs = times.argsort()
1181 times = times[timeArgs]
1182 fluxes = tmpDf[
"psfFlux"].to_numpy()[timeArgs]
1183 return (np.diff(fluxes) / np.diff(times)).max()
1185 column =
"{}_psfFluxMaxSlope".format(band)
1189 filterDiaSources.apply(_maxSlope),
1200 """Compute the mean of the dia source errors.
1203 ConfigClass = ErrMeanDiaPsfFluxConfig
1207 outputCols = [
"psfFluxErrMean"]
1221 """Compute the mean of the dia source errors.
1226 Summary object to store values in.
1227 diaSources : `pandas.DataFrame`
1228 DataFrame representing all diaSources associated with this
1230 filterDiaSources : `pandas.DataFrame`
1231 DataFrame representing diaSources associated with this
1232 diaObject that are observed in the band pass ``band``.
1234 Simple, string name of the filter for the flux being calculated.
1236 Any additional keyword arguments that may be passed to the plugin.
1238 column =
"{}_psfFluxErrMean".format(band)
1242 filterDiaSources.psfFluxErr.mean(),
1245 default_dtype=np.float32,
1255 """Compute fit a linear model to flux vs time.
1258 ConfigClass = LinearFitDiaPsfFluxConfig
1262 outputCols = [
"psfFluxLinearSlope",
"psfFluxLinearIntercept"]
1276 """Compute fit a linear model to flux vs time.
1281 Summary object to store values in.
1282 diaSources : `pandas.DataFrame`
1283 DataFrame representing all diaSources associated with this
1285 filterDiaSources : `pandas.DataFrame`
1286 DataFrame representing diaSources associated with this
1287 diaObject that are observed in the band pass ``band``.
1289 Simple, string name of the filter for the flux being calculated.
1291 Any additional keyword arguments that may be passed to the plugin.
1294 mName =
"{}_psfFluxLinearSlope".format(band)
1295 if mName
not in diaObjects.columns:
1296 diaObjects[mName] = np.nan
1297 bName =
"{}_psfFluxLinearIntercept".format(band)
1298 if bName
not in diaObjects.columns:
1299 diaObjects[bName] = np.nan
1300 dtype = diaObjects[mName].dtype
1303 tmpDf = df[~np.logical_or(
1304 np.isnan(df[
"psfFlux"]),
1305 np.logical_or(np.isnan(df[
"psfFluxErr"]),
1306 np.isnan(df[
"midpointMjdTai"])))]
1308 return pd.Series({mName: np.nan, bName: np.nan})
1309 fluxes = tmpDf[
"psfFlux"].to_numpy()
1310 errors = tmpDf[
"psfFluxErr"].to_numpy()
1311 times = tmpDf[
"midpointMjdTai"].to_numpy()
1312 A = np.array([times / errors, 1 / errors]).transpose()
1313 m, b = lsq_linear(A, fluxes / errors).x
1314 return pd.Series({mName: m, bName: b}, dtype=dtype)
1318 filterDiaSources.apply(_linearFit),
1329 """Compute the StetsonJ statistic on the DIA point source fluxes.
1332 ConfigClass = LinearFitDiaPsfFluxConfig
1335 inputCols = [
"psfFluxMean"]
1337 outputCols = [
"psfFluxStetsonJ"]
1351 """Compute the StetsonJ statistic on the DIA point source fluxes.
1356 Summary object to store values in.
1357 diaSources : `pandas.DataFrame`
1358 DataFrame representing all diaSources associated with this
1360 filterDiaSources : `pandas.DataFrame`
1361 DataFrame representing diaSources associated with this
1362 diaObject that are observed in the band pass ``band``.
1364 Simple, string name of the filter for the flux being calculated.
1366 Any additional keyword arguments that may be passed to the plugin.
1368 meanName =
"{}_psfFluxMean".format(band)
1371 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
1372 np.isnan(df[
"psfFluxErr"]))]
1375 fluxes = tmpDf[
"psfFlux"].to_numpy()
1376 errors = tmpDf[
"psfFluxErr"].to_numpy()
1381 diaObjects.at[tmpDf.diaObjectId.iat[0], meanName])
1383 column =
"{}_psfFluxStetsonJ".format(band)
1386 filterDiaSources.apply(_stetsonJ),
1391 """Compute the single band stetsonJ statistic.
1395 fluxes : `numpy.ndarray` (N,)
1396 Calibrated lightcurve flux values.
1397 errors : `numpy.ndarray` (N,)
1398 Errors on the calibrated lightcurve fluxes.
1400 Starting mean from previous plugin.
1405 stetsonJ statistic for the input fluxes and errors.
1409 .. [1] Stetson, P. B., "On the Automatic Determination of Light-Curve
1410 Parameters for Cepheid Variables", PASP, 108, 851S, 1996
1412 n_points = len(fluxes)
1415 np.sqrt(n_points / (n_points - 1)) * (fluxes - flux_mean) / errors)
1416 p_k = delta_val ** 2 - 1
1418 return np.mean(np.sign(p_k) * np.sqrt(np.fabs(p_k)))
1428 """Compute the stetson mean of the fluxes which down-weights outliers.
1430 Weighted biased on an error weighted difference scaled by a constant
1431 (1/``a``) and raised to the power beta. Higher betas more harshly
1432 penalize outliers and ``a`` sets the number of sigma where a weighted
1433 difference of 1 occurs.
1437 values : `numpy.dnarray`, (N,)
1438 Input values to compute the mean of.
1439 errors : `numpy.ndarray`, (N,)
1440 Errors on the input values.
1442 Starting mean value or None.
1444 Scalar down-weighting of the fractional difference. lower->more
1445 clipping. (Default value is 2.)
1447 Power law slope of the used to down-weight outliers. higher->more
1448 clipping. (Default value is 2.)
1450 Number of iterations of clipping.
1452 Fractional and absolute tolerance goal on the change in the mean
1453 before exiting early. (Default value is 1e-6)
1458 Weighted stetson mean result.
1462 .. [1] Stetson, P. B., "On the Automatic Determination of Light-Curve
1463 Parameters for Cepheid Variables", PASP, 108, 851S, 1996
1465 n_points = len(values)
1466 n_factor = np.sqrt(n_points / (n_points - 1))
1467 inv_var = 1 / errors ** 2
1470 mean = np.average(values, weights=inv_var)
1471 for iter_idx
in range(n_iter):
1472 chi = np.fabs(n_factor * (values - mean) / errors)
1473 tmp_mean = np.average(
1475 weights=inv_var / (1 + (chi / alpha) ** beta))
1476 diff = np.fabs(tmp_mean - mean)
1478 if diff / mean < tol
and diff < tol:
1489 """Compute the weighted mean and mean error on the point source fluxes
1490 forced photometered at the DiaSource location in the calibrated image.
1492 Additionally store number of usable data points.
1495 ConfigClass = WeightedMeanDiaPsfFluxConfig
1496 outputCols = [
"scienceFluxMean",
"scienceFluxMeanErr"]
1504 @catchWarnings(warns=[
"invalid value encountered",
1512 """Compute the weighted mean and mean error of the point source flux.
1517 Summary object to store values in.
1518 diaSources : `pandas.DataFrame`
1519 DataFrame representing all diaSources associated with this
1521 filterDiaSources : `pandas.DataFrame`
1522 DataFrame representing diaSources associated with this
1523 diaObject that are observed in the band pass ``band``.
1525 Simple, string name of the filter for the flux being calculated.
1527 Any additional keyword arguments that may be passed to the plugin.
1529 totMeanName =
"{}_scienceFluxMean".format(band)
1530 if totMeanName
not in diaObjects.columns:
1531 diaObjects[totMeanName] = np.nan
1532 totErrName =
"{}_scienceFluxMeanErr".format(band)
1533 if totErrName
not in diaObjects.columns:
1534 diaObjects[totErrName] = np.nan
1537 tmpDf = df[~np.logical_or(np.isnan(df[
"scienceFlux"]),
1538 np.isnan(df[
"scienceFluxErr"]))]
1539 tot_weight = np.nansum(1 / tmpDf[
"scienceFluxErr"] ** 2)
1540 fluxMean = np.nansum(tmpDf[
"scienceFlux"]
1541 / tmpDf[
"scienceFluxErr"] ** 2)
1542 fluxMean /= tot_weight
1543 fluxMeanErr = np.sqrt(1 / tot_weight)
1545 return pd.Series({totMeanName: fluxMean,
1546 totErrName: fluxMeanErr})
1548 df = filterDiaSources.apply(_meanFlux).astype(diaObjects.dtypes[[totMeanName, totErrName]])
1558 """Compute scatter of diaSource fluxes.
1561 ConfigClass = SigmaDiaPsfFluxConfig
1563 outputCols = [
"scienceFluxSigma"]
1577 """Compute the sigma fluxes of the point source flux measured on the
1583 Summary object to store values in.
1584 diaSources : `pandas.DataFrame`
1585 DataFrame representing all diaSources associated with this
1587 filterDiaSources : `pandas.DataFrame`
1588 DataFrame representing diaSources associated with this
1589 diaObject that are observed in the band pass ``band``.
1591 Simple, string name of the filter for the flux being calculated.
1593 Any additional keyword arguments that may be passed to the plugin.
1597 column =
"{}_scienceFluxSigma".format(band)
float FLUX_MOMENTS_CALCULATED
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaObjectId, **kwargs)
__init__(self, config, name, metadata)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band)
calculate_baluev_fap(time, n, maxPeriod, zmax)
generate_lsp_params(lsp_model, fbest, bands)
calculate(self, diaObjects, diaSources, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, **kwargs)
__init__(self, config, name, metadata, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
_stetson_J(self, fluxes, errors, mean=None)
_stetson_mean(self, values, errors, mean=None, alpha=2., beta=2., n_iter=20, tol=1e-6)
None __init__(self, separation, max_allowed_separation)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
float DEFAULT_CATALOGCALCULATION
compute_optimized_periodogram_grid(x0, oversampling_factor=5, nyquist_factor=100)
typeSafePandasAssignment(target, source, columns, default_dtype=np.float64, int_fill_value=0, force_int_to_float=False)
catchWarnings(_func=None, *, warns=[])
register(name, shouldApCorr=False, apCorrList=())