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
39from astropy.timeseries
import LombScargle
40from astropy.timeseries
import LombScargleMultiband
44from .diaCalculation
import (
45 DiaObjectCalculationPluginConfig,
46 DiaObjectCalculationPlugin)
47from .pluginRegistry
import register
50__all__ = (
"MeanDiaPositionConfig",
"MeanDiaPosition",
51 "HTMIndexDiaPosition",
"HTMIndexDiaPositionConfig",
52 "NumDiaSourcesDiaPlugin",
"NumDiaSourcesDiaPluginConfig",
53 "SimpleSourceFlagDiaPlugin",
"SimpleSourceFlagDiaPluginConfig",
54 "WeightedMeanDiaPsfFluxConfig",
"WeightedMeanDiaPsfFlux",
55 "PercentileDiaPsfFlux",
"PercentileDiaPsfFluxConfig",
56 "SigmaDiaPsfFlux",
"SigmaDiaPsfFluxConfig",
57 "Chi2DiaPsfFlux",
"Chi2DiaPsfFluxConfig",
58 "MadDiaPsfFlux",
"MadDiaPsfFluxConfig",
59 "SkewDiaPsfFlux",
"SkewDiaPsfFluxConfig",
60 "MinMaxDiaPsfFlux",
"MinMaxDiaPsfFluxConfig",
61 "MaxSlopeDiaPsfFlux",
"MaxSlopeDiaPsfFluxConfig",
62 "ErrMeanDiaPsfFlux",
"ErrMeanDiaPsfFluxConfig",
63 "LinearFitDiaPsfFlux",
"LinearFitDiaPsfFluxConfig",
64 "StetsonJDiaPsfFlux",
"StetsonJDiaPsfFluxConfig",
65 "WeightedMeanDiaTotFlux",
"WeightedMeanDiaTotFluxConfig",
66 "SigmaDiaTotFlux",
"SigmaDiaTotFluxConfig",
67 "LombScarglePeriodogram",
"LombScarglePeriodogramConfig",
68 "LombScarglePeriodogramMulti",
"LombScarglePeriodogramMultiConfig")
72 """Decorator for generically catching numpy warnings.
74 def decoratorCatchWarnings(func):
75 @functools.wraps(func)
76 def wrapperCatchWarnings(*args, **kwargs):
77 with warnings.catch_warnings():
79 warnings.filterwarnings(
"ignore", val)
80 return func(*args, **kwargs)
81 return wrapperCatchWarnings
84 return decoratorCatchWarnings
86 return decoratorCatchWarnings(_func)
93 default_dtype=np.float64,
96 force_int_to_float=False,
99 Assign from a source dataframe to a target dataframe in a type safe way.
103 target : `pd.DataFrame`
104 Target pandas dataframe.
105 source : `pd.DataFrame` or `pd.Series`
106 Grouped source dataframe.
107 columns : `list` [`str`]
108 List of columns to transfer.
109 default_dtype : `np.dtype`, optional
110 Default datatype (if not in target).
111 int_fill_value : `int`, optional
112 Fill value for integer columns to avoid pandas insisting
113 that everything should be float-ified as nans.
114 force_int_to_float : `bool`, optional
115 Force integer columns to float columns? Use this option
116 for backwards compatibility for old pandas misfeatures which
117 are expected by some downstream processes.
119 is_series = isinstance(source, pd.Series)
124 source_col = source[col]
126 matched_length =
False
127 if col
in target.columns:
128 target_dtype = target[col].dtype
129 matched_length = len(target) == len(source)
131 target_dtype = default_dtype
133 if (matched_length
or pd.api.types.is_float_dtype(target_dtype))
and not force_int_to_float:
136 target.loc[:, col] = source_col.astype(target_dtype)
142 target[col] = target[col].astype(np.float64)
144 target.loc[:, col] = source_col.astype(np.float64)
145 if not force_int_to_float:
147 target[col] = target[col].fillna(int_fill_value).astype(target_dtype)
152 Computes an optimized periodogram frequency grid for a given time series.
158 oversampling_factor : `int`, optional
159 The oversampling factor for frequency grid.
160 nyquist_factor : `int`, optional
161 The Nyquist factor for frequency grid.
165 frequencies : `array`
166 The computed optimized periodogram frequency grid.
170 baseline = np.max(x0) - np.min(x0)
173 frequency_resolution = 1. / baseline / oversampling_factor
175 num_frequencies = int(
176 0.5 * oversampling_factor * nyquist_factor * num_points)
177 frequencies = frequency_resolution + \
178 frequency_resolution * np.arange(num_frequencies)
187@
register(
"ap_lombScarglePeriodogram")
189 """Compute the single-band period of a DiaObject given a set of DiaSources.
191 ConfigClass = LombScarglePeriodogramConfig
194 outputCols = [
"period",
"power"]
201 @catchWarnings(warns=["All-NaN slice encountered"])
207 """Compute the periodogram.
211 diaObjects : `pandas.DataFrame`
212 Summary objects to store values in.
213 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
214 Catalog of DiaSources summarized by this DiaObject.
218 if (periodCol := f
"{band}_period")
not in diaObjects.columns:
219 diaObjects[periodCol] = np.nan
220 if (powerCol := f
"{band}_power")
not in diaObjects.columns:
221 diaObjects[powerCol] = np.nan
223 def _calculate_period(df, min_detections=5, nterms=1, oversampling_factor=5, nyquist_factor=100):
224 """Compute the Lomb-Scargle periodogram given a set of DiaSources.
228 df : `pandas.DataFrame`
230 min_detections : `int`, optional
231 The minimum number of detections.
232 nterms : `int`, optional
233 The number of terms in the Lomb-Scargle model.
234 oversampling_factor : `int`, optional
235 The oversampling factor for frequency grid.
236 nyquist_factor : `int`, optional
237 The Nyquist factor for frequency grid.
241 pd_tab : `pandas.Series`
242 The output DataFrame with the Lomb-Scargle parameters.
244 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
245 np.isnan(df[
"midpointMjdTai"]))]
247 if len(tmpDf) < min_detections:
248 return pd.Series({periodCol: np.nan, powerCol: np.nan})
250 time = tmpDf[
"midpointMjdTai"].to_numpy()
251 flux = tmpDf[
"psfFlux"].to_numpy()
252 flux_err = tmpDf[
"psfFluxErr"].to_numpy()
254 lsp = LombScargle(time, flux, dy=flux_err, nterms=nterms)
256 time, oversampling_factor=oversampling_factor, nyquist_factor=nyquist_factor)
258 power = lsp.power(f_grid)
260 return pd.Series({periodCol: period[np.argmax(power)],
261 powerCol: np.max(power)})
263 diaObjects.loc[:, [periodCol, powerCol]
264 ] = filterDiaSources.apply(_calculate_period)
271@
register(
"ap_lombScarglePeriodogramMulti")
273 """Compute the multi-band LombScargle periodogram of a DiaObject given a set of DiaSources.
275 ConfigClass = LombScarglePeriodogramMultiConfig
278 outputCols = [
"multiPeriod",
"multiPower",
279 "multiFap",
"multiAmp",
"multiPhase"]
288 """Calculate the False-Alarm probability using the Baluev approximation.
295 The number of detections.
297 The maximum period in the grid.
299 The maximum power in the grid.
303 fap_estimate : `float`
304 The False-Alarm probability Baluev approximation.
308 .. [1] Baluev, R. V. 2008, MNRAS, 385, 1279
309 .. [2] Süveges, M., Guy, L.P., Eyer, L., et al. 2015, MNRAS, 450, 2052
314 gam_ratio = math.factorial(int((n - 1)/2)) / math.factorial(int((n - 2)/2))
316 return gam_ratio * np.sqrt(
317 4*np.pi*statistics.variance(time)
318 ) * fu * (1-zmax)**((n-4)/2) * np.sqrt(zmax)
322 """Generate the Lomb-Scargle parameters.
325 lsp_model : `astropy.timeseries.LombScargleMultiband`
326 The Lomb-Scargle model.
330 The bands of the time series.
335 The amplitude of the time series.
337 The phase of the time series.
341 .. [1] VanderPlas, J. T., & Ivezic, Z. 2015, ApJ, 812, 18
343 best_params = lsp_model.model_parameters(fbest, units=
True)
345 name_params = [f
"theta_base_{i}" for i
in range(3)]
346 name_params += [f
"theta_band_{band}_{i}" for band
in np.unique(bands)
for i
in range(3)]
348 df_params = pd.DataFrame([best_params], columns=name_params)
350 unique_bands = np.unique(bands)
352 amplitude_band = [np.sqrt(df_params[f
"theta_band_{band}_1"]**2
353 + df_params[f
"theta_band_{band}_2"]**2)
354 for band
in unique_bands]
355 phase_bands = [np.arctan2(df_params[f
"theta_band_{band}_2"],
356 df_params[f
"theta_band_{band}_1"])
for band
in unique_bands]
358 amp = [a[0]
for a
in amplitude_band]
359 ph = [p[0]
for p
in phase_bands]
363 @catchWarnings(warns=["All-NaN slice encountered"])
368 """Compute the multi-band LombScargle periodogram of a DiaObject given
373 diaObjects : `pandas.DataFrame`
374 Summary objects to store values in.
375 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
376 Catalog of DiaSources summarized by this DiaObject.
378 Unused kwargs that are always passed to a plugin.
381 bands_arr = diaSources[
'band'].unique().values
382 unique_bands = np.unique(np.concatenate(bands_arr))
384 if (periodCol :=
"multiPeriod")
not in diaObjects.columns:
385 diaObjects[periodCol] = np.nan
386 if (powerCol :=
"multiPower")
not in diaObjects.columns:
387 diaObjects[powerCol] = np.nan
388 if (fapCol :=
"multiFap")
not in diaObjects.columns:
389 diaObjects[fapCol] = np.nan
391 phaseCol =
"multiPhase"
392 for i
in range(len(unique_bands)):
393 ampCol_band = f
"{unique_bands[i]}_{ampCol}"
394 if ampCol_band
not in diaObjects.columns:
395 diaObjects[ampCol_band] = np.nan
396 phaseCol_band = f
"{unique_bands[i]}_{phaseCol}"
397 if phaseCol_band
not in diaObjects.columns:
398 diaObjects[phaseCol_band] = np.nan
400 def _calculate_period_multi(df, all_unique_bands,
401 min_detections=9, oversampling_factor=5, nyquist_factor=100):
402 """Calculate the multi-band Lomb-Scargle periodogram.
406 df : `pandas.DataFrame`
408 all_unique_bands : `list` of `str`
409 List of all bands present in the diaSource table that is being worked on.
410 min_detections : `int`, optional
411 The minimum number of detections, including all bands.
412 oversampling_factor : `int`, optional
413 The oversampling factor for frequency grid.
414 nyquist_factor : `int`, optional
415 The Nyquist factor for frequency grid.
419 pd_tab : `pandas.Series`
420 The output DataFrame with the Lomb-Scargle parameters.
422 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
423 np.isnan(df[
"midpointMjdTai"]))]
425 if (len(tmpDf)) < min_detections:
426 pd_tab_nodet = pd.Series({periodCol: np.nan,
429 for band
in all_unique_bands:
430 pd_tab_nodet[f
"{band}_{ampCol}"] = np.nan
431 pd_tab_nodet[f
"{band}_{phaseCol}"] = np.nan
435 time = tmpDf[
"midpointMjdTai"].to_numpy()
436 flux = tmpDf[
"psfFlux"].to_numpy()
437 flux_err = tmpDf[
"psfFluxErr"].to_numpy()
438 bands = tmpDf[
"band"].to_numpy()
440 lsp = LombScargleMultiband(time, flux, bands, dy=flux_err,
441 nterms_base=1, nterms_band=1)
444 time, oversampling_factor=oversampling_factor, nyquist_factor=nyquist_factor)
446 power = lsp.power(f_grid)
449 time, len(time), period[np.argmax(power)], np.max(power))
453 pd_tab = pd.Series({periodCol: period[np.argmax(power)],
454 powerCol: np.max(power),
459 for band
in all_unique_bands:
460 pd_tab[f
"{band}_{ampCol}"] = np.nan
461 pd_tab[f
"{band}_{phaseCol}"] = np.nan
464 unique_bands = np.unique(bands)
465 for i
in range(len(unique_bands)):
466 pd_tab[f
"{unique_bands[i]}_{ampCol}"] = params_table_new[0][i]
467 pd_tab[f
"{unique_bands[i]}_{phaseCol}"] = params_table_new[1][i]
471 columns_list = [periodCol, powerCol, fapCol]
472 for i
in range(len(unique_bands)):
473 columns_list.append(f
"{unique_bands[i]}_{ampCol}")
474 columns_list.append(f
"{unique_bands[i]}_{phaseCol}")
476 diaObjects.loc[:, columns_list
477 ] = diaSources.apply(_calculate_period_multi, unique_bands)
486 """Compute the mean position of a DiaObject given a set of DiaSources.
489 ConfigClass = MeanDiaPositionConfig
493 outputCols = [
"ra",
"dec"]
501 """Compute the mean ra/dec position of the diaObject given the
506 diaObjects : `pandas.DataFrame`
507 Summary objects to store values in.
508 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
509 Catalog of DiaSources summarized by this DiaObject.
511 Any additional keyword arguments that may be passed to the plugin.
514 if outCol
not in diaObjects.columns:
515 diaObjects[outCol] = np.nan
517 def _computeMeanPos(df):
520 for idx, src
in df.iterrows()))
522 return pd.Series({
"ra": aveCoord.getRa().asDegrees(),
523 "dec": aveCoord.getDec().asDegrees()})
525 ans = diaSources.apply(_computeMeanPos)
531 htmLevel = pexConfig.Field(
533 doc=
"Level of the HTM pixelization.",
538@register("ap_HTMIndex")
540 """Compute the mean position of a DiaObject given a set of DiaSources.
544 This plugin was implemented to satisfy requirements of old APDB interface
545 which required ``pixelId`` column in DiaObject with HTM20 index. APDB
546 interface had migrated to not need that information, but we keep this
547 plugin in case it may be useful for something else.
549 ConfigClass = HTMIndexDiaPositionConfig
553 inputCols = [
"ra",
"dec"]
554 outputCols = [
"pixelId"]
558 DiaObjectCalculationPlugin.__init__(self, config, name, metadata)
566 """Compute the mean position of a DiaObject given a set of DiaSources
570 diaObjects : `pandas.dataFrame`
571 Summary objects to store values in and read ra/dec from.
573 Id of the diaObject to update.
575 Any additional keyword arguments that may be passed to the plugin.
578 diaObjects.at[diaObjectId,
"ra"] * geom.degrees,
579 diaObjects.at[diaObjectId,
"dec"] * geom.degrees)
580 diaObjects.at[diaObjectId,
"pixelId"] = self.
pixelator.index(
581 sphPoint.getVector())
590 """Compute the total number of DiaSources associated with this DiaObject.
593 ConfigClass = NumDiaSourcesDiaPluginConfig
594 outputCols = [
"nDiaSources"]
603 """Compute the total number of DiaSources associated with this DiaObject.
608 Summary object to store values in and read ra/dec from.
610 Any additional keyword arguments that may be passed to the plugin.
621 """Find if any DiaSource is flagged.
623 Set the DiaObject flag if any DiaSource is flagged.
626 ConfigClass = NumDiaSourcesDiaPluginConfig
627 outputCols = [
"flags"]
636 """Find if any DiaSource is flagged.
638 Set the DiaObject flag if any DiaSource is flagged.
643 Summary object to store values in and read ra/dec from.
645 Any additional keyword arguments that may be passed to the plugin.
656 """Compute the weighted mean and mean error on the point source fluxes
657 of the DiaSource measured on the difference image.
659 Additionally store number of usable data points.
662 ConfigClass = WeightedMeanDiaPsfFluxConfig
663 outputCols = [
"psfFluxMean",
"psfFluxMeanErr",
"psfFluxNdata"]
671 @catchWarnings(warns=[
"invalid value encountered",
679 """Compute the weighted mean and mean error of the point source flux.
684 Summary object to store values in.
685 diaSources : `pandas.DataFrame`
686 DataFrame representing all diaSources associated with this
688 filterDiaSources : `pandas.DataFrame`
689 DataFrame representing diaSources associated with this
690 diaObject that are observed in the band pass ``band``.
692 Simple, string name of the filter for the flux being calculated.
694 Any additional keyword arguments that may be passed to the plugin.
696 meanName =
"{}_psfFluxMean".format(band)
697 errName =
"{}_psfFluxMeanErr".format(band)
698 nDataName =
"{}_psfFluxNdata".format(band)
699 if meanName
not in diaObjects.columns:
700 diaObjects[meanName] = np.nan
701 if errName
not in diaObjects.columns:
702 diaObjects[errName] = np.nan
703 if nDataName
not in diaObjects.columns:
704 diaObjects[nDataName] = 0
706 def _weightedMean(df):
707 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
708 np.isnan(df[
"psfFluxErr"]))]
709 tot_weight = np.nansum(1 / tmpDf[
"psfFluxErr"] ** 2)
710 fluxMean = np.nansum(tmpDf[
"psfFlux"]
711 / tmpDf[
"psfFluxErr"] ** 2)
712 fluxMean /= tot_weight
714 fluxMeanErr = np.sqrt(1 / tot_weight)
717 nFluxData = len(tmpDf)
719 return pd.Series({meanName: fluxMean,
720 errName: fluxMeanErr,
721 nDataName: nFluxData},
723 df = filterDiaSources.apply(_weightedMean).astype(diaObjects.dtypes[[meanName, errName, nDataName]])
729 percentiles = pexConfig.ListField(
731 default=[5, 25, 50, 75, 95],
732 doc=
"Percentiles to calculate to compute values for. Should be "
737@register("ap_percentileFlux")
739 """Compute percentiles of diaSource fluxes.
742 ConfigClass = PercentileDiaPsfFluxConfig
748 def __init__(self, config, name, metadata, **kwargs):
749 DiaObjectCalculationPlugin.__init__(self,
754 self.
outputCols = [
"psfFluxPercentile{:02d}".format(percent)
755 for percent
in self.
config.percentiles]
761 @catchWarnings(warns=["All-NaN slice encountered"])
768 """Compute the percentile fluxes of the point source flux.
773 Summary object to store values in.
774 diaSources : `pandas.DataFrame`
775 DataFrame representing all diaSources associated with this
777 filterDiaSources : `pandas.DataFrame`
778 DataFrame representing diaSources associated with this
779 diaObject that are observed in the band pass ``band``.
781 Simple, string name of the filter for the flux being calculated.
783 Any additional keyword arguments that may be passed to the plugin.
786 for tilePercent
in self.
config.percentiles:
787 pTileName =
"{}_psfFluxPercentile{:02d}".format(band,
789 pTileNames.append(pTileName)
790 if pTileName
not in diaObjects.columns:
791 diaObjects[pTileName] = np.nan
793 def _fluxPercentiles(df):
794 pTiles = np.nanpercentile(df[
"psfFlux"], self.
config.percentiles)
796 dict((tileName, pTile)
797 for tileName, pTile
in zip(pTileNames, pTiles)))
801 filterDiaSources.apply(_fluxPercentiles),
812 """Compute scatter of diaSource fluxes.
815 ConfigClass = SigmaDiaPsfFluxConfig
817 outputCols = [
"psfFluxSigma"]
831 """Compute the sigma fluxes of the point source flux.
836 Summary object to store values in.
837 diaSources : `pandas.DataFrame`
838 DataFrame representing all diaSources associated with this
840 filterDiaSources : `pandas.DataFrame`
841 DataFrame representing diaSources associated with this
842 diaObject that are observed in the band pass ``band``.
844 Simple, string name of the filter for the flux being calculated.
846 Any additional keyword arguments that may be passed to the plugin.
850 column =
"{}_psfFluxSigma".format(band)
854 filterDiaSources.psfFlux.std(),
865 """Compute chi2 of diaSource fluxes.
868 ConfigClass = Chi2DiaPsfFluxConfig
871 inputCols = [
"psfFluxMean"]
873 outputCols = [
"psfFluxChi2"]
881 @catchWarnings(warns=["All-NaN slice encountered"])
888 """Compute the chi2 of the point source fluxes.
893 Summary object to store values in.
894 diaSources : `pandas.DataFrame`
895 DataFrame representing all diaSources associated with this
897 filterDiaSources : `pandas.DataFrame`
898 DataFrame representing diaSources associated with this
899 diaObject that are observed in the band pass ``band``.
901 Simple, string name of the filter for the flux being calculated.
903 Any additional keyword arguments that may be passed to the plugin.
905 meanName =
"{}_psfFluxMean".format(band)
906 column =
"{}_psfFluxChi2".format(band)
909 delta = (df[
"psfFlux"]
910 - diaObjects.at[df.diaObjectId.iat[0], meanName])
911 return np.nansum((delta / df[
"psfFluxErr"]) ** 2)
915 filterDiaSources.apply(_chi2),
926 """Compute median absolute deviation of diaSource fluxes.
929 ConfigClass = MadDiaPsfFluxConfig
933 outputCols = [
"psfFluxMAD"]
941 @catchWarnings(warns=["All-NaN slice encountered"])
948 """Compute the median absolute deviation of the point source fluxes.
953 Summary object to store values in.
954 diaSources : `pandas.DataFrame`
955 DataFrame representing all diaSources associated with this
957 filterDiaSources : `pandas.DataFrame`
958 DataFrame representing diaSources associated with this
959 diaObject that are observed in the band pass ``band``.
961 Simple, string name of the filter for the flux being calculated.
963 Any additional keyword arguments that may be passed to the plugin.
965 column =
"{}_psfFluxMAD".format(band)
969 filterDiaSources.psfFlux.apply(median_absolute_deviation, ignore_nan=
True),
980 """Compute the skew of diaSource fluxes.
983 ConfigClass = SkewDiaPsfFluxConfig
987 outputCols = [
"psfFluxSkew"]
1001 """Compute the skew of the point source fluxes.
1006 Summary object to store values in.
1007 diaSources : `pandas.DataFrame`
1008 DataFrame representing all diaSources associated with this
1010 filterDiaSources : `pandas.DataFrame`
1011 DataFrame representing diaSources associated with this
1012 diaObject that are observed in the band pass ``band``.
1014 Simple, string name of the filter for the flux being calculated.
1016 Any additional keyword arguments that may be passed to the plugin.
1018 column =
"{}_psfFluxSkew".format(band)
1022 filterDiaSources.psfFlux.skew(),
1033 """Compute min/max of diaSource fluxes.
1036 ConfigClass = MinMaxDiaPsfFluxConfig
1040 outputCols = [
"psfFluxMin",
"psfFluxMax"]
1054 """Compute min/max of the point source fluxes.
1059 Summary object to store values in.
1060 diaSources : `pandas.DataFrame`
1061 DataFrame representing all diaSources associated with this
1063 filterDiaSources : `pandas.DataFrame`
1064 DataFrame representing diaSources associated with this
1065 diaObject that are observed in the band pass ``band``.
1067 Simple, string name of the filter for the flux being calculated.
1069 Any additional keyword arguments that may be passed to the plugin.
1071 minName =
"{}_psfFluxMin".format(band)
1072 if minName
not in diaObjects.columns:
1073 diaObjects[minName] = np.nan
1074 maxName =
"{}_psfFluxMax".format(band)
1075 if maxName
not in diaObjects.columns:
1076 diaObjects[maxName] = np.nan
1080 filterDiaSources.psfFlux.min(),
1085 filterDiaSources.psfFlux.max(),
1096 """Compute the maximum ratio time ordered deltaFlux / deltaTime.
1099 ConfigClass = MinMaxDiaPsfFluxConfig
1103 outputCols = [
"psfFluxMaxSlope"]
1117 """Compute the maximum ratio time ordered deltaFlux / deltaTime.
1122 Summary object to store values in.
1123 diaSources : `pandas.DataFrame`
1124 DataFrame representing all diaSources associated with this
1126 filterDiaSources : `pandas.DataFrame`
1127 DataFrame representing diaSources associated with this
1128 diaObject that are observed in the band pass ``band``.
1130 Simple, string name of the filter for the flux being calculated.
1132 Any additional keyword arguments that may be passed to the plugin.
1136 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
1137 np.isnan(df[
"midpointMjdTai"]))]
1140 times = tmpDf[
"midpointMjdTai"].to_numpy()
1141 timeArgs = times.argsort()
1142 times = times[timeArgs]
1143 fluxes = tmpDf[
"psfFlux"].to_numpy()[timeArgs]
1144 return (np.diff(fluxes) / np.diff(times)).max()
1146 column =
"{}_psfFluxMaxSlope".format(band)
1150 filterDiaSources.apply(_maxSlope),
1161 """Compute the mean of the dia source errors.
1164 ConfigClass = ErrMeanDiaPsfFluxConfig
1168 outputCols = [
"psfFluxErrMean"]
1182 """Compute the mean of the dia source errors.
1187 Summary object to store values in.
1188 diaSources : `pandas.DataFrame`
1189 DataFrame representing all diaSources associated with this
1191 filterDiaSources : `pandas.DataFrame`
1192 DataFrame representing diaSources associated with this
1193 diaObject that are observed in the band pass ``band``.
1195 Simple, string name of the filter for the flux being calculated.
1197 Any additional keyword arguments that may be passed to the plugin.
1199 column =
"{}_psfFluxErrMean".format(band)
1203 filterDiaSources.psfFluxErr.mean(),
1206 default_dtype=np.float32,
1216 """Compute fit a linear model to flux vs time.
1219 ConfigClass = LinearFitDiaPsfFluxConfig
1223 outputCols = [
"psfFluxLinearSlope",
"psfFluxLinearIntercept"]
1237 """Compute fit a linear model to flux vs time.
1242 Summary object to store values in.
1243 diaSources : `pandas.DataFrame`
1244 DataFrame representing all diaSources associated with this
1246 filterDiaSources : `pandas.DataFrame`
1247 DataFrame representing diaSources associated with this
1248 diaObject that are observed in the band pass ``band``.
1250 Simple, string name of the filter for the flux being calculated.
1252 Any additional keyword arguments that may be passed to the plugin.
1255 mName =
"{}_psfFluxLinearSlope".format(band)
1256 if mName
not in diaObjects.columns:
1257 diaObjects[mName] = np.nan
1258 bName =
"{}_psfFluxLinearIntercept".format(band)
1259 if bName
not in diaObjects.columns:
1260 diaObjects[bName] = np.nan
1261 dtype = diaObjects[mName].dtype
1264 tmpDf = df[~np.logical_or(
1265 np.isnan(df[
"psfFlux"]),
1266 np.logical_or(np.isnan(df[
"psfFluxErr"]),
1267 np.isnan(df[
"midpointMjdTai"])))]
1269 return pd.Series({mName: np.nan, bName: np.nan})
1270 fluxes = tmpDf[
"psfFlux"].to_numpy()
1271 errors = tmpDf[
"psfFluxErr"].to_numpy()
1272 times = tmpDf[
"midpointMjdTai"].to_numpy()
1273 A = np.array([times / errors, 1 / errors]).transpose()
1274 m, b = lsq_linear(A, fluxes / errors).x
1275 return pd.Series({mName: m, bName: b}, dtype=dtype)
1279 filterDiaSources.apply(_linearFit),
1290 """Compute the StetsonJ statistic on the DIA point source fluxes.
1293 ConfigClass = LinearFitDiaPsfFluxConfig
1296 inputCols = [
"psfFluxMean"]
1298 outputCols = [
"psfFluxStetsonJ"]
1312 """Compute the StetsonJ statistic on the DIA point source fluxes.
1317 Summary object to store values in.
1318 diaSources : `pandas.DataFrame`
1319 DataFrame representing all diaSources associated with this
1321 filterDiaSources : `pandas.DataFrame`
1322 DataFrame representing diaSources associated with this
1323 diaObject that are observed in the band pass ``band``.
1325 Simple, string name of the filter for the flux being calculated.
1327 Any additional keyword arguments that may be passed to the plugin.
1329 meanName =
"{}_psfFluxMean".format(band)
1332 tmpDf = df[~np.logical_or(np.isnan(df[
"psfFlux"]),
1333 np.isnan(df[
"psfFluxErr"]))]
1336 fluxes = tmpDf[
"psfFlux"].to_numpy()
1337 errors = tmpDf[
"psfFluxErr"].to_numpy()
1342 diaObjects.at[tmpDf.diaObjectId.iat[0], meanName])
1344 column =
"{}_psfFluxStetsonJ".format(band)
1347 filterDiaSources.apply(_stetsonJ),
1352 """Compute the single band stetsonJ statistic.
1356 fluxes : `numpy.ndarray` (N,)
1357 Calibrated lightcurve flux values.
1358 errors : `numpy.ndarray` (N,)
1359 Errors on the calibrated lightcurve fluxes.
1361 Starting mean from previous plugin.
1366 stetsonJ statistic for the input fluxes and errors.
1370 .. [1] Stetson, P. B., "On the Automatic Determination of Light-Curve
1371 Parameters for Cepheid Variables", PASP, 108, 851S, 1996
1373 n_points = len(fluxes)
1376 np.sqrt(n_points / (n_points - 1)) * (fluxes - flux_mean) / errors)
1377 p_k = delta_val ** 2 - 1
1379 return np.mean(np.sign(p_k) * np.sqrt(np.fabs(p_k)))
1389 """Compute the stetson mean of the fluxes which down-weights outliers.
1391 Weighted biased on an error weighted difference scaled by a constant
1392 (1/``a``) and raised to the power beta. Higher betas more harshly
1393 penalize outliers and ``a`` sets the number of sigma where a weighted
1394 difference of 1 occurs.
1398 values : `numpy.dnarray`, (N,)
1399 Input values to compute the mean of.
1400 errors : `numpy.ndarray`, (N,)
1401 Errors on the input values.
1403 Starting mean value or None.
1405 Scalar down-weighting of the fractional difference. lower->more
1406 clipping. (Default value is 2.)
1408 Power law slope of the used to down-weight outliers. higher->more
1409 clipping. (Default value is 2.)
1411 Number of iterations of clipping.
1413 Fractional and absolute tolerance goal on the change in the mean
1414 before exiting early. (Default value is 1e-6)
1419 Weighted stetson mean result.
1423 .. [1] Stetson, P. B., "On the Automatic Determination of Light-Curve
1424 Parameters for Cepheid Variables", PASP, 108, 851S, 1996
1426 n_points = len(values)
1427 n_factor = np.sqrt(n_points / (n_points - 1))
1428 inv_var = 1 / errors ** 2
1431 mean = np.average(values, weights=inv_var)
1432 for iter_idx
in range(n_iter):
1433 chi = np.fabs(n_factor * (values - mean) / errors)
1434 tmp_mean = np.average(
1436 weights=inv_var / (1 + (chi / alpha) ** beta))
1437 diff = np.fabs(tmp_mean - mean)
1439 if diff / mean < tol
and diff < tol:
1450 """Compute the weighted mean and mean error on the point source fluxes
1451 forced photometered at the DiaSource location in the calibrated image.
1453 Additionally store number of usable data points.
1456 ConfigClass = WeightedMeanDiaPsfFluxConfig
1457 outputCols = [
"scienceFluxMean",
"scienceFluxMeanErr"]
1465 @catchWarnings(warns=[
"invalid value encountered",
1473 """Compute the weighted mean and mean error of the point source flux.
1478 Summary object to store values in.
1479 diaSources : `pandas.DataFrame`
1480 DataFrame representing all diaSources associated with this
1482 filterDiaSources : `pandas.DataFrame`
1483 DataFrame representing diaSources associated with this
1484 diaObject that are observed in the band pass ``band``.
1486 Simple, string name of the filter for the flux being calculated.
1488 Any additional keyword arguments that may be passed to the plugin.
1490 totMeanName =
"{}_scienceFluxMean".format(band)
1491 if totMeanName
not in diaObjects.columns:
1492 diaObjects[totMeanName] = np.nan
1493 totErrName =
"{}_scienceFluxMeanErr".format(band)
1494 if totErrName
not in diaObjects.columns:
1495 diaObjects[totErrName] = np.nan
1498 tmpDf = df[~np.logical_or(np.isnan(df[
"scienceFlux"]),
1499 np.isnan(df[
"scienceFluxErr"]))]
1500 tot_weight = np.nansum(1 / tmpDf[
"scienceFluxErr"] ** 2)
1501 fluxMean = np.nansum(tmpDf[
"scienceFlux"]
1502 / tmpDf[
"scienceFluxErr"] ** 2)
1503 fluxMean /= tot_weight
1504 fluxMeanErr = np.sqrt(1 / tot_weight)
1506 return pd.Series({totMeanName: fluxMean,
1507 totErrName: fluxMeanErr})
1509 df = filterDiaSources.apply(_meanFlux).astype(diaObjects.dtypes[[totMeanName, totErrName]])
1519 """Compute scatter of diaSource fluxes.
1522 ConfigClass = SigmaDiaPsfFluxConfig
1524 outputCols = [
"scienceFluxSigma"]
1538 """Compute the sigma fluxes of the point source flux measured on the
1544 Summary object to store values in.
1545 diaSources : `pandas.DataFrame`
1546 DataFrame representing all diaSources associated with this
1548 filterDiaSources : `pandas.DataFrame`
1549 DataFrame representing diaSources associated with this
1550 diaObject that are observed in the band pass ``band``.
1552 Simple, string name of the filter for the flux being calculated.
1554 Any additional keyword arguments that may be passed to the plugin.
1558 column =
"{}_scienceFluxSigma".format(band)
Point in an unspecified spherical coordinate system.
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)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
calculate(self, diaObjects, diaSources, filterDiaSources, band, **kwargs)
float DEFAULT_CATALOGCALCULATION
HtmPixelization provides HTM indexing of points and regions.
SpherePoint averageSpherePoint(std::vector< SpherePoint > const &coords)
Return the average of a list of coordinates.
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=())