LSST Applications g04e9c324dd+8c5ae1fdc5,g134cb467dc+1b3060144d,g18429d2f64+f642bf4753,g199a45376c+0ba108daf9,g1fd858c14a+2dcf163641,g262e1987ae+7b8c96d2ca,g29ae962dfc+3bd6ecb08a,g2cef7863aa+aef1011c0b,g35bb328faa+8c5ae1fdc5,g3fd5ace14f+53e1a9e7c5,g4595892280+fef73a337f,g47891489e3+2efcf17695,g4d44eb3520+642b70b07e,g53246c7159+8c5ae1fdc5,g67b6fd64d1+2efcf17695,g67fd3c3899+b70e05ef52,g74acd417e5+317eb4c7d4,g786e29fd12+668abc6043,g87389fa792+8856018cbb,g89139ef638+2efcf17695,g8d7436a09f+3be3c13596,g8ea07a8fe4+9f5ccc88ac,g90f42f885a+a4e7b16d9b,g97be763408+ad77d7208f,g9dd6db0277+b70e05ef52,ga681d05dcb+a3f46e7fff,gabf8522325+735880ea63,gac2eed3f23+2efcf17695,gb89ab40317+2efcf17695,gbf99507273+8c5ae1fdc5,gd8ff7fe66e+b70e05ef52,gdab6d2f7ff+317eb4c7d4,gdc713202bf+b70e05ef52,gdfd2d52018+b10e285e0f,ge365c994fd+310e8507c4,ge410e46f29+2efcf17695,geaed405ab2+562b3308c0,gffca2db377+8c5ae1fdc5,w.2025.35
LSST Data Management Base Package
Loading...
Searching...
No Matches
diaCalculationPlugins.py
Go to the documentation of this file.
1# This file is part of ap_association.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22"""Plugins for use in DiaSource summary statistics.
23
24Output columns must be
25as defined in the schema of the Apdb both in name and units.
26"""
27
28import functools
29import warnings
30
31from astropy.stats import median_absolute_deviation
32import numpy as np
33import pandas as pd
34from scipy.optimize import lsq_linear
35
36import lsst.geom as geom
37import lsst.pex.config as pexConfig
38import lsst.sphgeom as sphgeom
39from astropy.timeseries import LombScargle
40from astropy.timeseries import LombScargleMultiband
41import math
42import statistics
43
44from .diaCalculation import (
45 DiaObjectCalculationPluginConfig,
46 DiaObjectCalculationPlugin)
47from .pluginRegistry import register
48
49
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")
69
70
71def catchWarnings(_func=None, *, warns=[]):
72 """Decorator for generically catching numpy warnings.
73 """
74 def decoratorCatchWarnings(func):
75 @functools.wraps(func)
76 def wrapperCatchWarnings(*args, **kwargs):
77 with warnings.catch_warnings():
78 for val in warns:
79 warnings.filterwarnings("ignore", val)
80 return func(*args, **kwargs)
81 return wrapperCatchWarnings
82
83 if _func is None:
84 return decoratorCatchWarnings
85 else:
86 return decoratorCatchWarnings(_func)
87
88
89def compute_optimized_periodogram_grid(x0, oversampling_factor=5, nyquist_factor=100):
90 """
91 Computes an optimized periodogram frequency grid for a given time series.
92
93 Parameters
94 ----------
95 x0 : `array`
96 The input time axis.
97 oversampling_factor : `int`, optional
98 The oversampling factor for frequency grid.
99 nyquist_factor : `int`, optional
100 The Nyquist factor for frequency grid.
101
102 Returns
103 -------
104 frequencies : `array`
105 The computed optimized periodogram frequency grid.
106 """
107
108 num_points = len(x0)
109 baseline = np.max(x0) - np.min(x0)
110
111 # Calculate the frequency resolution based on oversampling factor and baseline
112 frequency_resolution = 1. / baseline / oversampling_factor
113
114 num_frequencies = int(
115 0.5 * oversampling_factor * nyquist_factor * num_points)
116 frequencies = frequency_resolution + \
117 frequency_resolution * np.arange(num_frequencies)
118
119 return frequencies
120
121
123 pass
124
125
126@register("ap_lombScarglePeriodogram")
128 """Compute the single-band period of a DiaObject given a set of DiaSources.
129 """
130 ConfigClass = LombScarglePeriodogramConfig
131
132 plugType = "multi"
133 outputCols = ["period", "power"]
134 needsFilter = True
135
136 @classmethod
139
140 @catchWarnings(warns=["All-NaN slice encountered"])
141 def calculate(self,
142 diaObjects,
143 diaSources,
144 filterDiaSources,
145 band):
146 """Compute the periodogram.
147
148 Parameters
149 ----------
150 diaObjects : `pandas.DataFrame`
151 Summary objects to store values in.
152 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
153 Catalog of DiaSources summarized by this DiaObject.
154 """
155
156 # Check and initialize output columns in diaObjects.
157 if (periodCol := f"{band}_period") not in diaObjects.columns:
158 diaObjects[periodCol] = np.nan
159 if (powerCol := f"{band}_power") not in diaObjects.columns:
160 diaObjects[powerCol] = np.nan
161
162 def _calculate_period(df, min_detections=5, nterms=1, oversampling_factor=5, nyquist_factor=100):
163 """Compute the Lomb-Scargle periodogram given a set of DiaSources.
164
165 Parameters
166 ----------
167 df : `pandas.DataFrame`
168 The input DataFrame.
169 min_detections : `int`, optional
170 The minimum number of detections.
171 nterms : `int`, optional
172 The number of terms in the Lomb-Scargle model.
173 oversampling_factor : `int`, optional
174 The oversampling factor for frequency grid.
175 nyquist_factor : `int`, optional
176 The Nyquist factor for frequency grid.
177
178 Returns
179 -------
180 pd_tab : `pandas.Series`
181 The output DataFrame with the Lomb-Scargle parameters.
182 """
183 tmpDf = df[~np.logical_or(np.isnan(df["psfFlux"]),
184 np.isnan(df["midpointMjdTai"]))]
185
186 if len(tmpDf) < min_detections:
187 return pd.Series({periodCol: np.nan, powerCol: np.nan})
188
189 time = tmpDf["midpointMjdTai"].to_numpy()
190 flux = tmpDf["psfFlux"].to_numpy()
191 flux_err = tmpDf["psfFluxErr"].to_numpy()
192
193 lsp = LombScargle(time, flux, dy=flux_err, nterms=nterms)
195 time, oversampling_factor=oversampling_factor, nyquist_factor=nyquist_factor)
196 period = 1/f_grid
197 power = lsp.power(f_grid)
198
199 return pd.Series({periodCol: period[np.argmax(power)],
200 powerCol: np.max(power)})
201
202 diaObjects.loc[:, [periodCol, powerCol]
203 ] = filterDiaSources.apply(_calculate_period)
204
205
207 pass
208
209
210@register("ap_lombScarglePeriodogramMulti")
212 """Compute the multi-band LombScargle periodogram of a DiaObject given a set of DiaSources.
213 """
214 ConfigClass = LombScarglePeriodogramMultiConfig
215
216 plugType = "multi"
217 outputCols = ["multiPeriod", "multiPower",
218 "multiFap", "multiAmp", "multiPhase"]
219 needsFilter = True
220
221 @classmethod
224
225 @staticmethod
226 def calculate_baluev_fap(time, n, maxPeriod, zmax):
227 """Calculate the False-Alarm probability using the Baluev approximation.
228
229 Parameters
230 ----------
231 time : `array`
232 The input time axis.
233 n : `int`
234 The number of detections.
235 maxPeriod : `float`
236 The maximum period in the grid.
237 zmax : `float`
238 The maximum power in the grid.
239
240 Returns
241 -------
242 fap_estimate : `float`
243 The False-Alarm probability Baluev approximation.
244
245 Notes
246 ----------
247 .. [1] Baluev, R. V. 2008, MNRAS, 385, 1279
248 .. [2] Süveges, M., Guy, L.P., Eyer, L., et al. 2015, MNRAS, 450, 2052
249 """
250 if n <= 2:
251 return np.nan
252
253 gam_ratio = math.factorial(int((n - 1)/2)) / math.factorial(int((n - 2)/2))
254 fu = 1/maxPeriod
255 return gam_ratio * np.sqrt(
256 4*np.pi*statistics.variance(time)
257 ) * fu * (1-zmax)**((n-4)/2) * np.sqrt(zmax)
258
259 @staticmethod
260 def generate_lsp_params(lsp_model, fbest, bands):
261 """Generate the Lomb-Scargle parameters.
262 Parameters
263 ----------
264 lsp_model : `astropy.timeseries.LombScargleMultiband`
265 The Lomb-Scargle model.
266 fbest : `float`
267 The best period.
268 bands : `array`
269 The bands of the time series.
270
271 Returns
272 -------
273 Amp : `array`
274 The amplitude of the time series.
275 Ph : `array`
276 The phase of the time series.
277
278 Notes
279 ----------
280 .. [1] VanderPlas, J. T., & Ivezic, Z. 2015, ApJ, 812, 18
281 """
282 best_params = lsp_model.model_parameters(fbest, units=True)
283
284 name_params = [f"theta_base_{i}" for i in range(3)]
285 name_params += [f"theta_band_{band}_{i}" for band in np.unique(bands) for i in range(3)]
286
287 df_params = pd.DataFrame([best_params], columns=name_params)
288
289 unique_bands = np.unique(bands)
290
291 amplitude_band = [np.sqrt(df_params[f"theta_band_{band}_1"]**2
292 + df_params[f"theta_band_{band}_2"]**2)
293 for band in unique_bands]
294 phase_bands = [np.arctan2(df_params[f"theta_band_{band}_2"],
295 df_params[f"theta_band_{band}_1"]) for band in unique_bands]
296
297 amp = [a[0] for a in amplitude_band]
298 ph = [p[0] for p in phase_bands]
299
300 return amp, ph
301
302 @catchWarnings(warns=["All-NaN slice encountered"])
303 def calculate(self,
304 diaObjects,
305 diaSources,
306 **kwargs):
307 """Compute the multi-band LombScargle periodogram of a DiaObject given
308 a set of DiaSources.
309
310 Parameters
311 ----------
312 diaObjects : `pandas.DataFrame`
313 Summary objects to store values in.
314 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
315 Catalog of DiaSources summarized by this DiaObject.
316 **kwargs : `dict`
317 Unused kwargs that are always passed to a plugin.
318 """
319
320 bands_arr = diaSources['band'].unique().values
321 unique_bands = np.unique(np.concatenate(bands_arr))
322 # Check and initialize output columns in diaObjects.
323 if (periodCol := "multiPeriod") not in diaObjects.columns:
324 diaObjects[periodCol] = np.nan
325 if (powerCol := "multiPower") not in diaObjects.columns:
326 diaObjects[powerCol] = np.nan
327 if (fapCol := "multiFap") not in diaObjects.columns:
328 diaObjects[fapCol] = np.nan
329 ampCol = "multiAmp"
330 phaseCol = "multiPhase"
331 for i in range(len(unique_bands)):
332 ampCol_band = f"{unique_bands[i]}_{ampCol}"
333 if ampCol_band not in diaObjects.columns:
334 diaObjects[ampCol_band] = np.nan
335 phaseCol_band = f"{unique_bands[i]}_{phaseCol}"
336 if phaseCol_band not in diaObjects.columns:
337 diaObjects[phaseCol_band] = np.nan
338
339 def _calculate_period_multi(df, all_unique_bands,
340 min_detections=9, oversampling_factor=5, nyquist_factor=100):
341 """Calculate the multi-band Lomb-Scargle periodogram.
342
343 Parameters
344 ----------
345 df : `pandas.DataFrame`
346 The input DataFrame.
347 all_unique_bands : `list` of `str`
348 List of all bands present in the diaSource table that is being worked on.
349 min_detections : `int`, optional
350 The minimum number of detections, including all bands.
351 oversampling_factor : `int`, optional
352 The oversampling factor for frequency grid.
353 nyquist_factor : `int`, optional
354 The Nyquist factor for frequency grid.
355
356 Returns
357 -------
358 pd_tab : `pandas.Series`
359 The output DataFrame with the Lomb-Scargle parameters.
360 """
361 tmpDf = df[~np.logical_or(np.isnan(df["psfFlux"]),
362 np.isnan(df["midpointMjdTai"]))]
363
364 if (len(tmpDf)) < min_detections:
365 pd_tab_nodet = pd.Series({periodCol: np.nan,
366 powerCol: np.nan,
367 fapCol: np.nan})
368 for band in all_unique_bands:
369 pd_tab_nodet[f"{band}_{ampCol}"] = np.nan
370 pd_tab_nodet[f"{band}_{phaseCol}"] = np.nan
371
372 return pd_tab_nodet
373
374 time = tmpDf["midpointMjdTai"].to_numpy()
375 flux = tmpDf["psfFlux"].to_numpy()
376 flux_err = tmpDf["psfFluxErr"].to_numpy()
377 bands = tmpDf["band"].to_numpy()
378
379 lsp = LombScargleMultiband(time, flux, bands, dy=flux_err,
380 nterms_base=1, nterms_band=1)
381
383 time, oversampling_factor=oversampling_factor, nyquist_factor=nyquist_factor)
384 period = 1/f_grid
385 power = lsp.power(f_grid)
386
387 fap_estimate = self.calculate_baluev_fap(
388 time, len(time), period[np.argmax(power)], np.max(power))
389
390 params_table_new = self.generate_lsp_params(lsp, f_grid[np.argmax(power)], bands)
391
392 pd_tab = pd.Series({periodCol: period[np.argmax(power)],
393 powerCol: np.max(power),
394 fapCol: fap_estimate
395 })
396
397 # Initialize the per-band amplitude/phase columns as NaNs
398 for band in all_unique_bands:
399 pd_tab[f"{band}_{ampCol}"] = np.nan
400 pd_tab[f"{band}_{phaseCol}"] = np.nan
401
402 # Populate the values of only the bands that have data for this diaSource
403 unique_bands = np.unique(bands)
404 for i in range(len(unique_bands)):
405 pd_tab[f"{unique_bands[i]}_{ampCol}"] = params_table_new[0][i]
406 pd_tab[f"{unique_bands[i]}_{phaseCol}"] = params_table_new[1][i]
407
408 return pd_tab
409
410 columns_list = [periodCol, powerCol, fapCol]
411 for i in range(len(unique_bands)):
412 columns_list.append(f"{unique_bands[i]}_{ampCol}")
413 columns_list.append(f"{unique_bands[i]}_{phaseCol}")
414
415 diaObjects.loc[:, columns_list
416 ] = diaSources.apply(_calculate_period_multi, unique_bands)
417
418
420 pass
421
422
423@register("ap_meanPosition")
425 """Compute the mean position of a DiaObject given a set of DiaSources.
426 """
427
428 ConfigClass = MeanDiaPositionConfig
429
430 plugType = 'multi'
431
432 outputCols = ["ra", "dec"]
433 needsFilter = False
434
435 @classmethod
438
439 def calculate(self, diaObjects, diaSources, **kwargs):
440 """Compute the mean ra/dec position of the diaObject given the
441 diaSource locations.
442
443 Parameters
444 ----------
445 diaObjects : `pandas.DataFrame`
446 Summary objects to store values in.
447 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
448 Catalog of DiaSources summarized by this DiaObject.
449 **kwargs
450 Any additional keyword arguments that may be passed to the plugin.
451 """
452 for outCol in self.outputCols:
453 if outCol not in diaObjects.columns:
454 diaObjects[outCol] = np.nan
455
456 def _computeMeanPos(df):
457 aveCoord = geom.averageSpherePoint(
458 list(geom.SpherePoint(src["ra"], src["dec"], geom.degrees)
459 for idx, src in df.iterrows()))
460
461 return pd.Series({"ra": aveCoord.getRa().asDegrees(),
462 "dec": aveCoord.getDec().asDegrees()})
463
464 ans = diaSources.apply(_computeMeanPos)
465 diaObjects.loc[:, ["ra", "dec"]] = ans
466
467
469
470 htmLevel = pexConfig.Field(
471 dtype=int,
472 doc="Level of the HTM pixelization.",
473 default=20,
474 )
475
476
477@register("ap_HTMIndex")
479 """Compute the mean position of a DiaObject given a set of DiaSources.
480
481 Notes
482 -----
483 This plugin was implemented to satisfy requirements of old APDB interface
484 which required ``pixelId`` column in DiaObject with HTM20 index. APDB
485 interface had migrated to not need that information, but we keep this
486 plugin in case it may be useful for something else.
487 """
488 ConfigClass = HTMIndexDiaPositionConfig
489
490 plugType = 'single'
491
492 inputCols = ["ra", "dec"]
493 outputCols = ["pixelId"]
494 needsFilter = False
495
496 def __init__(self, config, name, metadata):
497 DiaObjectCalculationPlugin.__init__(self, config, name, metadata)
499
500 @classmethod
502 return cls.FLUX_MOMENTS_CALCULATED
503
504 def calculate(self, diaObjects, diaObjectId, **kwargs):
505 """Compute the mean position of a DiaObject given a set of DiaSources
506
507 Parameters
508 ----------
509 diaObjects : `pandas.dataFrame`
510 Summary objects to store values in and read ra/dec from.
511 diaObjectId : `int`
512 Id of the diaObject to update.
513 **kwargs
514 Any additional keyword arguments that may be passed to the plugin.
515 """
516 sphPoint = geom.SpherePoint(
517 diaObjects.at[diaObjectId, "ra"] * geom.degrees,
518 diaObjects.at[diaObjectId, "dec"] * geom.degrees)
519 diaObjects.at[diaObjectId, "pixelId"] = self.pixelator.index(
520 sphPoint.getVector())
521
522
524 pass
525
526
527@register("ap_nDiaSources")
529 """Compute the total number of DiaSources associated with this DiaObject.
530 """
531
532 ConfigClass = NumDiaSourcesDiaPluginConfig
533 outputCols = ["nDiaSources"]
534 plugType = "multi"
535 needsFilter = False
536
537 @classmethod
540
541 def calculate(self, diaObjects, diaSources, **kwargs):
542 """Compute the total number of DiaSources associated with this DiaObject.
543
544 Parameters
545 ----------
546 diaObject : `dict`
547 Summary object to store values in and read ra/dec from.
548 **kwargs
549 Any additional keyword arguments that may be passed to the plugin.
550 """
551 dtype = diaObjects["nDiaSources"].dtype
552 diaObjects.loc[:, "nDiaSources"] = diaSources.diaObjectId.count().astype(dtype)
553
554
556 pass
557
558
559@register("ap_diaObjectFlag")
561 """Find if any DiaSource is flagged.
562
563 Set the DiaObject flag if any DiaSource is flagged.
564 """
565
566 ConfigClass = NumDiaSourcesDiaPluginConfig
567 outputCols = ["flags"]
568 plugType = "multi"
569 needsFilter = False
570
571 @classmethod
574
575 def calculate(self, diaObjects, diaSources, **kwargs):
576 """Find if any DiaSource is flagged.
577
578 Set the DiaObject flag if any DiaSource is flagged.
579
580 Parameters
581 ----------
582 diaObject : `dict`
583 Summary object to store values in and read ra/dec from.
584 **kwargs
585 Any additional keyword arguments that may be passed to the plugin.
586 """
587 dtype = diaObjects["flags"].dtype
588 diaObjects.loc[:, "flags"] = diaSources.flags.any().astype(dtype)
589
590
597 """Compute the weighted mean and mean error on the point source fluxes
598 of the DiaSource measured on the difference image.
599
600 Additionally store number of usable data points.
601 """
602
603 ConfigClass = WeightedMeanDiaPsfFluxConfig
604 outputCols = ["psfFluxMean", "psfFluxMeanErr", "psfFluxNdata"]
605 plugType = "multi"
606 needsFilter = True
607
608 @classmethod
611
612 @catchWarnings(warns=["invalid value encountered",
613 "divide by zero"])
614 def calculate(self,
615 diaObjects,
616 diaSources,
617 filterDiaSources,
618 band,
619 **kwargs):
620 """Compute the weighted mean and mean error of the point source flux.
621
622 Parameters
623 ----------
624 diaObject : `dict`
625 Summary object to store values in.
626 diaSources : `pandas.DataFrame`
627 DataFrame representing all diaSources associated with this
628 diaObject.
629 filterDiaSources : `pandas.DataFrame`
630 DataFrame representing diaSources associated with this
631 diaObject that are observed in the band pass ``band``.
632 band : `str`
633 Simple, string name of the filter for the flux being calculated.
634 **kwargs
635 Any additional keyword arguments that may be passed to the plugin.
636 """
637 meanName = "{}_psfFluxMean".format(band)
638 errName = "{}_psfFluxMeanErr".format(band)
639 nDataName = "{}_psfFluxNdata".format(band)
640 if meanName not in diaObjects.columns:
641 diaObjects[meanName] = np.nan
642 if errName not in diaObjects.columns:
643 diaObjects[errName] = np.nan
644 if nDataName not in diaObjects.columns:
645 diaObjects[nDataName] = 0
646
647 def _weightedMean(df):
648 tmpDf = df[~np.logical_or(np.isnan(df["psfFlux"]),
649 np.isnan(df["psfFluxErr"]))]
650 tot_weight = np.nansum(1 / tmpDf["psfFluxErr"] ** 2)
651 fluxMean = np.nansum(tmpDf["psfFlux"]
652 / tmpDf["psfFluxErr"] ** 2)
653 fluxMean /= tot_weight
654 if tot_weight > 0:
655 fluxMeanErr = np.sqrt(1 / tot_weight)
656 else:
657 fluxMeanErr = np.nan
658 nFluxData = len(tmpDf)
659
660 return pd.Series({meanName: fluxMean,
661 errName: fluxMeanErr,
662 nDataName: nFluxData},
663 dtype="object")
664 df = filterDiaSources.apply(_weightedMean).astype(diaObjects.dtypes[[meanName, errName, nDataName]])
665
666 diaObjects.loc[:, [meanName, errName, nDataName]] = df
667
668
670 percentiles = pexConfig.ListField(
671 dtype=int,
672 default=[5, 25, 50, 75, 95],
673 doc="Percentiles to calculate to compute values for. Should be "
674 "integer values."
675 )
676
677
678@register("ap_percentileFlux")
680 """Compute percentiles of diaSource fluxes.
681 """
682
683 ConfigClass = PercentileDiaPsfFluxConfig
684 # Output columns are created upon instantiation of the class.
685 outputCols = []
686 plugType = "multi"
687 needsFilter = True
688
689 def __init__(self, config, name, metadata, **kwargs):
690 DiaObjectCalculationPlugin.__init__(self,
691 config,
692 name,
693 metadata,
694 **kwargs)
695 self.outputCols = ["psfFluxPercentile{:02d}".format(percent)
696 for percent in self.config.percentiles]
697
698 @classmethod
701
702 @catchWarnings(warns=["All-NaN slice encountered"])
703 def calculate(self,
704 diaObjects,
705 diaSources,
706 filterDiaSources,
707 band,
708 **kwargs):
709 """Compute the percentile fluxes of the point source flux.
710
711 Parameters
712 ----------
713 diaObject : `dict`
714 Summary object to store values in.
715 diaSources : `pandas.DataFrame`
716 DataFrame representing all diaSources associated with this
717 diaObject.
718 filterDiaSources : `pandas.DataFrame`
719 DataFrame representing diaSources associated with this
720 diaObject that are observed in the band pass ``band``.
721 band : `str`
722 Simple, string name of the filter for the flux being calculated.
723 **kwargs
724 Any additional keyword arguments that may be passed to the plugin.
725 """
726 pTileNames = []
727 dtype = None
728 for tilePercent in self.config.percentiles:
729 pTileName = "{}_psfFluxPercentile{:02d}".format(band,
730 tilePercent)
731 pTileNames.append(pTileName)
732 if pTileName not in diaObjects.columns:
733 diaObjects[pTileName] = np.nan
734 elif dtype is None:
735 dtype = diaObjects[pTileName].dtype
736
737 def _fluxPercentiles(df):
738 pTiles = np.nanpercentile(df["psfFlux"], self.config.percentiles)
739 return pd.Series(
740 dict((tileName, pTile)
741 for tileName, pTile in zip(pTileNames, pTiles)))
742 if dtype is None:
743 diaObjects.loc[:, pTileNames] = filterDiaSources.apply(_fluxPercentiles)
744 else:
745 diaObjects.loc[:, pTileNames] = filterDiaSources.apply(_fluxPercentiles).astype(dtype)
746
747
749 pass
750
751
752@register("ap_sigmaFlux")
754 """Compute scatter of diaSource fluxes.
755 """
756
757 ConfigClass = SigmaDiaPsfFluxConfig
758 # Output columns are created upon instantiation of the class.
759 outputCols = ["psfFluxSigma"]
760 plugType = "multi"
761 needsFilter = True
762
763 @classmethod
766
767 def calculate(self,
768 diaObjects,
769 diaSources,
770 filterDiaSources,
771 band,
772 **kwargs):
773 """Compute the sigma fluxes of the point source flux.
774
775 Parameters
776 ----------
777 diaObject : `dict`
778 Summary object to store values in.
779 diaSources : `pandas.DataFrame`
780 DataFrame representing all diaSources associated with this
781 diaObject.
782 filterDiaSources : `pandas.DataFrame`
783 DataFrame representing diaSources associated with this
784 diaObject that are observed in the band pass ``band``.
785 band : `str`
786 Simple, string name of the filter for the flux being calculated.
787 **kwargs
788 Any additional keyword arguments that may be passed to the plugin.
789 """
790 # Set "delta degrees of freedom (ddf)" to 1 to calculate the unbiased
791 # estimator of scatter (i.e. 'N - 1' instead of 'N').
792 column = "{}_psfFluxSigma".format(band)
793 if column in diaObjects:
794 dtype = diaObjects[column].dtype
795 diaObjects.loc[:, column] = filterDiaSources.psfFlux.std().astype(dtype)
796 else:
797 diaObjects.loc[:, column] = filterDiaSources.psfFlux.std()
798
799
801 pass
802
803
804@register("ap_chi2Flux")
806 """Compute chi2 of diaSource fluxes.
807 """
808
809 ConfigClass = Chi2DiaPsfFluxConfig
810
811 # Required input Cols
812 inputCols = ["psfFluxMean"]
813 # Output columns are created upon instantiation of the class.
814 outputCols = ["psfFluxChi2"]
815 plugType = "multi"
816 needsFilter = True
817
818 @classmethod
820 return cls.FLUX_MOMENTS_CALCULATED
821
822 @catchWarnings(warns=["All-NaN slice encountered"])
823 def calculate(self,
824 diaObjects,
825 diaSources,
826 filterDiaSources,
827 band,
828 **kwargs):
829 """Compute the chi2 of the point source fluxes.
830
831 Parameters
832 ----------
833 diaObject : `dict`
834 Summary object to store values in.
835 diaSources : `pandas.DataFrame`
836 DataFrame representing all diaSources associated with this
837 diaObject.
838 filterDiaSources : `pandas.DataFrame`
839 DataFrame representing diaSources associated with this
840 diaObject that are observed in the band pass ``band``.
841 band : `str`
842 Simple, string name of the filter for the flux being calculated.
843 **kwargs
844 Any additional keyword arguments that may be passed to the plugin.
845 """
846 meanName = "{}_psfFluxMean".format(band)
847 column = "{}_psfFluxChi2".format(band)
848
849 def _chi2(df):
850 delta = (df["psfFlux"]
851 - diaObjects.at[df.diaObjectId.iat[0], meanName])
852 return np.nansum((delta / df["psfFluxErr"]) ** 2)
853
854 if column in diaObjects:
855 dtype = diaObjects[column].dtype
856 diaObjects.loc[:, column] = filterDiaSources.apply(_chi2).astype(dtype)
857 else:
858 diaObjects.loc[:, column] = filterDiaSources.apply(_chi2)
859
860
862 pass
863
864
865@register("ap_madFlux")
867 """Compute median absolute deviation of diaSource fluxes.
868 """
869
870 ConfigClass = MadDiaPsfFluxConfig
871
872 # Required input Cols
873 # Output columns are created upon instantiation of the class.
874 outputCols = ["psfFluxMAD"]
875 plugType = "multi"
876 needsFilter = True
877
878 @classmethod
881
882 @catchWarnings(warns=["All-NaN slice encountered"])
883 def calculate(self,
884 diaObjects,
885 diaSources,
886 filterDiaSources,
887 band,
888 **kwargs):
889 """Compute the median absolute deviation of the point source fluxes.
890
891 Parameters
892 ----------
893 diaObject : `dict`
894 Summary object to store values in.
895 diaSources : `pandas.DataFrame`
896 DataFrame representing all diaSources associated with this
897 diaObject.
898 filterDiaSources : `pandas.DataFrame`
899 DataFrame representing diaSources associated with this
900 diaObject that are observed in the band pass ``band``.
901 band : `str`
902 Simple, string name of the filter for the flux being calculated.
903 **kwargs
904 Any additional keyword arguments that may be passed to the plugin.
905 """
906 column = "{}_psfFluxMAD".format(band)
907 if column in diaObjects:
908 dtype = diaObjects[column].dtype
909 diaObjects.loc[:, column] = \
910 filterDiaSources.psfFlux.apply(median_absolute_deviation,
911 ignore_nan=True).astype(dtype)
912 else:
913 diaObjects.loc[:, column] = \
914 filterDiaSources.psfFlux.apply(median_absolute_deviation,
915 ignore_nan=True)
916
917
919 pass
920
921
922@register("ap_skewFlux")
924 """Compute the skew of diaSource fluxes.
925 """
926
927 ConfigClass = SkewDiaPsfFluxConfig
928
929 # Required input Cols
930 # Output columns are created upon instantiation of the class.
931 outputCols = ["psfFluxSkew"]
932 plugType = "multi"
933 needsFilter = True
934
935 @classmethod
938
939 def calculate(self,
940 diaObjects,
941 diaSources,
942 filterDiaSources,
943 band,
944 **kwargs):
945 """Compute the skew of the point source fluxes.
946
947 Parameters
948 ----------
949 diaObject : `dict`
950 Summary object to store values in.
951 diaSources : `pandas.DataFrame`
952 DataFrame representing all diaSources associated with this
953 diaObject.
954 filterDiaSources : `pandas.DataFrame`
955 DataFrame representing diaSources associated with this
956 diaObject that are observed in the band pass ``band``.
957 band : `str`
958 Simple, string name of the filter for the flux being calculated.
959 **kwargs
960 Any additional keyword arguments that may be passed to the plugin.
961 """
962 column = "{}_psfFluxSkew".format(band)
963 if column in diaObjects:
964 dtype = diaObjects[column].dtype
965 diaObjects.loc[:, column] = filterDiaSources.psfFlux.skew().astype(dtype)
966 else:
967 diaObjects.loc[:, column] = filterDiaSources.psfFlux.skew()
968
969
971 pass
972
973
974@register("ap_minMaxFlux")
976 """Compute min/max of diaSource fluxes.
977 """
978
979 ConfigClass = MinMaxDiaPsfFluxConfig
980
981 # Required input Cols
982 # Output columns are created upon instantiation of the class.
983 outputCols = ["psfFluxMin", "psfFluxMax"]
984 plugType = "multi"
985 needsFilter = True
986
987 @classmethod
990
991 def calculate(self,
992 diaObjects,
993 diaSources,
994 filterDiaSources,
995 band,
996 **kwargs):
997 """Compute min/max of the point source fluxes.
998
999 Parameters
1000 ----------
1001 diaObject : `dict`
1002 Summary object to store values in.
1003 diaSources : `pandas.DataFrame`
1004 DataFrame representing all diaSources associated with this
1005 diaObject.
1006 filterDiaSources : `pandas.DataFrame`
1007 DataFrame representing diaSources associated with this
1008 diaObject that are observed in the band pass ``band``.
1009 band : `str`
1010 Simple, string name of the filter for the flux being calculated.
1011 **kwargs
1012 Any additional keyword arguments that may be passed to the plugin.
1013 """
1014 minName = "{}_psfFluxMin".format(band)
1015 if minName not in diaObjects.columns:
1016 diaObjects[minName] = np.nan
1017 maxName = "{}_psfFluxMax".format(band)
1018 if maxName not in diaObjects.columns:
1019 diaObjects[maxName] = np.nan
1020
1021 dtype = diaObjects[minName].dtype
1022 diaObjects.loc[:, minName] = filterDiaSources.psfFlux.min().astype(dtype)
1023 diaObjects.loc[:, maxName] = filterDiaSources.psfFlux.max().astype(dtype)
1024
1025
1027 pass
1028
1029
1030@register("ap_maxSlopeFlux")
1032 """Compute the maximum ratio time ordered deltaFlux / deltaTime.
1033 """
1034
1035 ConfigClass = MinMaxDiaPsfFluxConfig
1036
1037 # Required input Cols
1038 # Output columns are created upon instantiation of the class.
1039 outputCols = ["psfFluxMaxSlope"]
1040 plugType = "multi"
1041 needsFilter = True
1042
1043 @classmethod
1046
1047 def calculate(self,
1048 diaObjects,
1049 diaSources,
1050 filterDiaSources,
1051 band,
1052 **kwargs):
1053 """Compute the maximum ratio time ordered deltaFlux / deltaTime.
1054
1055 Parameters
1056 ----------
1057 diaObject : `dict`
1058 Summary object to store values in.
1059 diaSources : `pandas.DataFrame`
1060 DataFrame representing all diaSources associated with this
1061 diaObject.
1062 filterDiaSources : `pandas.DataFrame`
1063 DataFrame representing diaSources associated with this
1064 diaObject that are observed in the band pass ``band``.
1065 band : `str`
1066 Simple, string name of the filter for the flux being calculated.
1067 **kwargs
1068 Any additional keyword arguments that may be passed to the plugin.
1069 """
1070
1071 def _maxSlope(df):
1072 tmpDf = df[~np.logical_or(np.isnan(df["psfFlux"]),
1073 np.isnan(df["midpointMjdTai"]))]
1074 if len(tmpDf) < 2:
1075 return np.nan
1076 times = tmpDf["midpointMjdTai"].to_numpy()
1077 timeArgs = times.argsort()
1078 times = times[timeArgs]
1079 fluxes = tmpDf["psfFlux"].to_numpy()[timeArgs]
1080 return (np.diff(fluxes) / np.diff(times)).max()
1081
1082 column = "{}_psfFluxMaxSlope".format(band)
1083 if column in diaObjects:
1084 dtype = diaObjects[column].dtype
1085 diaObjects.loc[:, column] = filterDiaSources.apply(_maxSlope).astype(dtype)
1086 else:
1087 diaObjects.loc[:, column] = filterDiaSources.apply(_maxSlope)
1088
1089
1091 pass
1092
1093
1094@register("ap_meanErrFlux")
1096 """Compute the mean of the dia source errors.
1097 """
1098
1099 ConfigClass = ErrMeanDiaPsfFluxConfig
1100
1101 # Required input Cols
1102 # Output columns are created upon instantiation of the class.
1103 outputCols = ["psfFluxErrMean"]
1104 plugType = "multi"
1105 needsFilter = True
1106
1107 @classmethod
1110
1111 def calculate(self,
1112 diaObjects,
1113 diaSources,
1114 filterDiaSources,
1115 band,
1116 **kwargs):
1117 """Compute the mean of the dia source errors.
1118
1119 Parameters
1120 ----------
1121 diaObject : `dict`
1122 Summary object to store values in.
1123 diaSources : `pandas.DataFrame`
1124 DataFrame representing all diaSources associated with this
1125 diaObject.
1126 filterDiaSources : `pandas.DataFrame`
1127 DataFrame representing diaSources associated with this
1128 diaObject that are observed in the band pass ``band``.
1129 band : `str`
1130 Simple, string name of the filter for the flux being calculated.
1131 **kwargs
1132 Any additional keyword arguments that may be passed to the plugin.
1133 """
1134 column = "{}_psfFluxErrMean".format(band)
1135 if column in diaObjects:
1136 dtype = diaObjects[column].dtype
1137 diaObjects.loc[:, column] = filterDiaSources.psfFluxErr.mean().astype(dtype)
1138 else:
1139 diaObjects.loc[:, column] = filterDiaSources.psfFluxErr.mean()
1140
1141
1143 pass
1144
1145
1146@register("ap_linearFit")
1148 """Compute fit a linear model to flux vs time.
1149 """
1150
1151 ConfigClass = LinearFitDiaPsfFluxConfig
1152
1153 # Required input Cols
1154 # Output columns are created upon instantiation of the class.
1155 outputCols = ["psfFluxLinearSlope", "psfFluxLinearIntercept"]
1156 plugType = "multi"
1157 needsFilter = True
1158
1159 @classmethod
1162
1163 def calculate(self,
1164 diaObjects,
1165 diaSources,
1166 filterDiaSources,
1167 band,
1168 **kwargs):
1169 """Compute fit a linear model to flux vs time.
1170
1171 Parameters
1172 ----------
1173 diaObject : `dict`
1174 Summary object to store values in.
1175 diaSources : `pandas.DataFrame`
1176 DataFrame representing all diaSources associated with this
1177 diaObject.
1178 filterDiaSources : `pandas.DataFrame`
1179 DataFrame representing diaSources associated with this
1180 diaObject that are observed in the band pass ``band``.
1181 band : `str`
1182 Simple, string name of the filter for the flux being calculated.
1183 **kwargs
1184 Any additional keyword arguments that may be passed to the plugin.
1185 """
1186
1187 mName = "{}_psfFluxLinearSlope".format(band)
1188 if mName not in diaObjects.columns:
1189 diaObjects[mName] = np.nan
1190 bName = "{}_psfFluxLinearIntercept".format(band)
1191 if bName not in diaObjects.columns:
1192 diaObjects[bName] = np.nan
1193 dtype = diaObjects[mName].dtype
1194
1195 def _linearFit(df):
1196 tmpDf = df[~np.logical_or(
1197 np.isnan(df["psfFlux"]),
1198 np.logical_or(np.isnan(df["psfFluxErr"]),
1199 np.isnan(df["midpointMjdTai"])))]
1200 if len(tmpDf) < 2:
1201 return pd.Series({mName: np.nan, bName: np.nan})
1202 fluxes = tmpDf["psfFlux"].to_numpy()
1203 errors = tmpDf["psfFluxErr"].to_numpy()
1204 times = tmpDf["midpointMjdTai"].to_numpy()
1205 A = np.array([times / errors, 1 / errors]).transpose()
1206 m, b = lsq_linear(A, fluxes / errors).x
1207 return pd.Series({mName: m, bName: b}, dtype=dtype)
1208
1209 diaObjects.loc[:, [mName, bName]] = filterDiaSources.apply(_linearFit)
1210
1211
1213 pass
1214
1215
1216@register("ap_stetsonJ")
1218 """Compute the StetsonJ statistic on the DIA point source fluxes.
1219 """
1220
1221 ConfigClass = LinearFitDiaPsfFluxConfig
1222
1223 # Required input Cols
1224 inputCols = ["psfFluxMean"]
1225 # Output columns are created upon instantiation of the class.
1226 outputCols = ["psfFluxStetsonJ"]
1227 plugType = "multi"
1228 needsFilter = True
1229
1230 @classmethod
1232 return cls.FLUX_MOMENTS_CALCULATED
1233
1234 def calculate(self,
1235 diaObjects,
1236 diaSources,
1237 filterDiaSources,
1238 band,
1239 **kwargs):
1240 """Compute the StetsonJ statistic on the DIA point source fluxes.
1241
1242 Parameters
1243 ----------
1244 diaObject : `dict`
1245 Summary object to store values in.
1246 diaSources : `pandas.DataFrame`
1247 DataFrame representing all diaSources associated with this
1248 diaObject.
1249 filterDiaSources : `pandas.DataFrame`
1250 DataFrame representing diaSources associated with this
1251 diaObject that are observed in the band pass ``band``.
1252 band : `str`
1253 Simple, string name of the filter for the flux being calculated.
1254 **kwargs
1255 Any additional keyword arguments that may be passed to the plugin.
1256 """
1257 meanName = "{}_psfFluxMean".format(band)
1258
1259 def _stetsonJ(df):
1260 tmpDf = df[~np.logical_or(np.isnan(df["psfFlux"]),
1261 np.isnan(df["psfFluxErr"]))]
1262 if len(tmpDf) < 2:
1263 return np.nan
1264 fluxes = tmpDf["psfFlux"].to_numpy()
1265 errors = tmpDf["psfFluxErr"].to_numpy()
1266
1267 return self._stetson_J(
1268 fluxes,
1269 errors,
1270 diaObjects.at[tmpDf.diaObjectId.iat[0], meanName])
1271
1272 column = "{}_psfFluxStetsonJ".format(band)
1273 if column in diaObjects:
1274 dtype = diaObjects[column].dtype
1275 diaObjects.loc[:, column] = filterDiaSources.apply(_stetsonJ).astype(dtype)
1276 else:
1277 diaObjects.loc[:, column] = filterDiaSources.apply(_stetsonJ)
1278
1279 def _stetson_J(self, fluxes, errors, mean=None):
1280 """Compute the single band stetsonJ statistic.
1281
1282 Parameters
1283 ----------
1284 fluxes : `numpy.ndarray` (N,)
1285 Calibrated lightcurve flux values.
1286 errors : `numpy.ndarray` (N,)
1287 Errors on the calibrated lightcurve fluxes.
1288 mean : `float`
1289 Starting mean from previous plugin.
1290
1291 Returns
1292 -------
1293 stetsonJ : `float`
1294 stetsonJ statistic for the input fluxes and errors.
1295
1296 References
1297 ----------
1298 .. [1] Stetson, P. B., "On the Automatic Determination of Light-Curve
1299 Parameters for Cepheid Variables", PASP, 108, 851S, 1996
1300 """
1301 n_points = len(fluxes)
1302 flux_mean = self._stetson_mean(fluxes, errors, mean)
1303 delta_val = (
1304 np.sqrt(n_points / (n_points - 1)) * (fluxes - flux_mean) / errors)
1305 p_k = delta_val ** 2 - 1
1306
1307 return np.mean(np.sign(p_k) * np.sqrt(np.fabs(p_k)))
1308
1310 values,
1311 errors,
1312 mean=None,
1313 alpha=2.,
1314 beta=2.,
1315 n_iter=20,
1316 tol=1e-6):
1317 """Compute the stetson mean of the fluxes which down-weights outliers.
1318
1319 Weighted biased on an error weighted difference scaled by a constant
1320 (1/``a``) and raised to the power beta. Higher betas more harshly
1321 penalize outliers and ``a`` sets the number of sigma where a weighted
1322 difference of 1 occurs.
1323
1324 Parameters
1325 ----------
1326 values : `numpy.dnarray`, (N,)
1327 Input values to compute the mean of.
1328 errors : `numpy.ndarray`, (N,)
1329 Errors on the input values.
1330 mean : `float`
1331 Starting mean value or None.
1332 alpha : `float`
1333 Scalar down-weighting of the fractional difference. lower->more
1334 clipping. (Default value is 2.)
1335 beta : `float`
1336 Power law slope of the used to down-weight outliers. higher->more
1337 clipping. (Default value is 2.)
1338 n_iter : `int`
1339 Number of iterations of clipping.
1340 tol : `float`
1341 Fractional and absolute tolerance goal on the change in the mean
1342 before exiting early. (Default value is 1e-6)
1343
1344 Returns
1345 -------
1346 mean : `float`
1347 Weighted stetson mean result.
1348
1349 References
1350 ----------
1351 .. [1] Stetson, P. B., "On the Automatic Determination of Light-Curve
1352 Parameters for Cepheid Variables", PASP, 108, 851S, 1996
1353 """
1354 n_points = len(values)
1355 n_factor = np.sqrt(n_points / (n_points - 1))
1356 inv_var = 1 / errors ** 2
1357
1358 if mean is None:
1359 mean = np.average(values, weights=inv_var)
1360 for iter_idx in range(n_iter):
1361 chi = np.fabs(n_factor * (values - mean) / errors)
1362 tmp_mean = np.average(
1363 values,
1364 weights=inv_var / (1 + (chi / alpha) ** beta))
1365 diff = np.fabs(tmp_mean - mean)
1366 mean = tmp_mean
1367 if diff / mean < tol and diff < tol:
1368 break
1369 return mean
1370
1371
1373 pass
1374
1375
1376@register("ap_meanTotFlux")
1378 """Compute the weighted mean and mean error on the point source fluxes
1379 forced photometered at the DiaSource location in the calibrated image.
1380
1381 Additionally store number of usable data points.
1382 """
1383
1384 ConfigClass = WeightedMeanDiaPsfFluxConfig
1385 outputCols = ["scienceFluxMean", "scienceFluxMeanErr"]
1386 plugType = "multi"
1387 needsFilter = True
1388
1389 @classmethod
1392
1393 @catchWarnings(warns=["invalid value encountered",
1394 "divide by zero"])
1395 def calculate(self,
1396 diaObjects,
1397 diaSources,
1398 filterDiaSources,
1399 band,
1400 **kwargs):
1401 """Compute the weighted mean and mean error of the point source flux.
1402
1403 Parameters
1404 ----------
1405 diaObject : `dict`
1406 Summary object to store values in.
1407 diaSources : `pandas.DataFrame`
1408 DataFrame representing all diaSources associated with this
1409 diaObject.
1410 filterDiaSources : `pandas.DataFrame`
1411 DataFrame representing diaSources associated with this
1412 diaObject that are observed in the band pass ``band``.
1413 band : `str`
1414 Simple, string name of the filter for the flux being calculated.
1415 **kwargs
1416 Any additional keyword arguments that may be passed to the plugin.
1417 """
1418 totMeanName = "{}_scienceFluxMean".format(band)
1419 if totMeanName not in diaObjects.columns:
1420 diaObjects[totMeanName] = np.nan
1421 totErrName = "{}_scienceFluxMeanErr".format(band)
1422 if totErrName not in diaObjects.columns:
1423 diaObjects[totErrName] = np.nan
1424
1425 def _meanFlux(df):
1426 tmpDf = df[~np.logical_or(np.isnan(df["scienceFlux"]),
1427 np.isnan(df["scienceFluxErr"]))]
1428 tot_weight = np.nansum(1 / tmpDf["scienceFluxErr"] ** 2)
1429 fluxMean = np.nansum(tmpDf["scienceFlux"]
1430 / tmpDf["scienceFluxErr"] ** 2)
1431 fluxMean /= tot_weight
1432 fluxMeanErr = np.sqrt(1 / tot_weight)
1433
1434 return pd.Series({totMeanName: fluxMean,
1435 totErrName: fluxMeanErr})
1436
1437 df = filterDiaSources.apply(_meanFlux).astype(diaObjects.dtypes[[totMeanName, totErrName]])
1438 diaObjects.loc[:, [totMeanName, totErrName]] = df
1439
1440
1442 pass
1443
1444
1445@register("ap_sigmaTotFlux")
1447 """Compute scatter of diaSource fluxes.
1448 """
1449
1450 ConfigClass = SigmaDiaPsfFluxConfig
1451 # Output columns are created upon instantiation of the class.
1452 outputCols = ["scienceFluxSigma"]
1453 plugType = "multi"
1454 needsFilter = True
1455
1456 @classmethod
1459
1460 def calculate(self,
1461 diaObjects,
1462 diaSources,
1463 filterDiaSources,
1464 band,
1465 **kwargs):
1466 """Compute the sigma fluxes of the point source flux measured on the
1467 calibrated image.
1468
1469 Parameters
1470 ----------
1471 diaObject : `dict`
1472 Summary object to store values in.
1473 diaSources : `pandas.DataFrame`
1474 DataFrame representing all diaSources associated with this
1475 diaObject.
1476 filterDiaSources : `pandas.DataFrame`
1477 DataFrame representing diaSources associated with this
1478 diaObject that are observed in the band pass ``band``.
1479 band : `str`
1480 Simple, string name of the filter for the flux being calculated.
1481 **kwargs
1482 Any additional keyword arguments that may be passed to the plugin.
1483 """
1484 # Set "delta degrees of freedom (ddf)" to 1 to calculate the unbiased
1485 # estimator of scatter (i.e. 'N - 1' instead of 'N').
1486 column = "{}_scienceFluxSigma".format(band)
1487 if column in diaObjects:
1488 dtype = diaObjects[column].dtype
1489 diaObjects.loc[:, column] = filterDiaSources.scienceFlux.std().astype(dtype)
1490 else:
1491
1492 diaObjects.loc[:, column] = filterDiaSources.scienceFlux.std()
Point in an unspecified spherical coordinate system.
Definition SpherePoint.h:57
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, filterDiaSources, band)
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, filterDiaSources, band, **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, filterDiaSources, band, **kwargs)
_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)
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)
register(name, shouldApCorr=False, apCorrList=())