LSST Applications g070148d5b3+33e5256705,g0d53e28543+25c8b88941,g0da5cf3356+2dd1178308,g1081da9e2a+62d12e78cb,g17e5ecfddb+7e422d6136,g1c76d35bf8+ede3a706f7,g295839609d+225697d880,g2e2c1a68ba+cc1f6f037e,g2ffcdf413f+853cd4dcde,g38293774b4+62d12e78cb,g3b44f30a73+d953f1ac34,g48ccf36440+885b902d19,g4b2f1765b6+7dedbde6d2,g5320a0a9f6+0c5d6105b6,g56b687f8c9+ede3a706f7,g5c4744a4d9+ef6ac23297,g5ffd174ac0+0c5d6105b6,g6075d09f38+66af417445,g667d525e37+2ced63db88,g670421136f+2ced63db88,g71f27ac40c+2ced63db88,g774830318a+463cbe8d1f,g7876bc68e5+1d137996f1,g7985c39107+62d12e78cb,g7fdac2220c+0fd8241c05,g96f01af41f+368e6903a7,g9ca82378b8+2ced63db88,g9d27549199+ef6ac23297,gabe93b2c52+e3573e3735,gb065e2a02a+3dfbe639da,gbc3249ced9+0c5d6105b6,gbec6a3398f+0c5d6105b6,gc9534b9d65+35b9f25267,gd01420fc67+0c5d6105b6,geee7ff78d7+a14128c129,gf63283c776+ede3a706f7,gfed783d017+0c5d6105b6,w.2022.47
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
39
40from .diaCalculation import (
41 DiaObjectCalculationPluginConfig,
42 DiaObjectCalculationPlugin)
43from .pluginRegistry import register
44
45__all__ = ("MeanDiaPositionConfig", "MeanDiaPosition",
46 "HTMIndexDiaPosition", "HTMIndexDiaPositionConfig",
47 "NumDiaSourcesDiaPlugin", "NumDiaSourcesDiaPluginConfig",
48 "SimpleSourceFlagDiaPlugin", "SimpleSourceFlagDiaPluginConfig",
49 "WeightedMeanDiaPsFluxConfig", "WeightedMeanDiaPsFlux",
50 "PercentileDiaPsFlux", "PercentileDiaPsFluxConfig",
51 "SigmaDiaPsFlux", "SigmaDiaPsFluxConfig",
52 "Chi2DiaPsFlux", "Chi2DiaPsFluxConfig",
53 "MadDiaPsFlux", "MadDiaPsFluxConfig",
54 "SkewDiaPsFlux", "SkewDiaPsFluxConfig",
55 "MinMaxDiaPsFlux", "MinMaxDiaPsFluxConfig",
56 "MaxSlopeDiaPsFlux", "MaxSlopeDiaPsFluxConfig",
57 "ErrMeanDiaPsFlux", "ErrMeanDiaPsFluxConfig",
58 "LinearFitDiaPsFlux", "LinearFitDiaPsFluxConfig",
59 "StetsonJDiaPsFlux", "StetsonJDiaPsFluxConfig",
60 "WeightedMeanDiaTotFlux", "WeightedMeanDiaTotFluxConfig",
61 "SigmaDiaTotFlux", "SigmaDiaTotFluxConfig")
62
63
64def catchWarnings(_func=None, *, warns=[]):
65 """Decorator for generically catching numpy warnings.
66 """
67 def decoratorCatchWarnings(func):
68 @functools.wraps(func)
69 def wrapperCatchWarnings(*args, **kwargs):
70 with warnings.catch_warnings():
71 for val in warns:
72 warnings.filterwarnings("ignore", val)
73 return func(*args, **kwargs)
74 return wrapperCatchWarnings
75
76 if _func is None:
77 return decoratorCatchWarnings
78 else:
79 return decoratorCatchWarnings(_func)
80
81
83 pass
84
85
86@register("ap_meanPosition")
88 """Compute the mean position of a DiaObject given a set of DiaSources.
89 """
90
91 ConfigClass = MeanDiaPositionConfig
92
93 plugType = 'multi'
94
95 outputCols = ["ra", "decl", "radecTai"]
96 needsFilter = False
97
98 @classmethod
101
102 def calculate(self, diaObjects, diaSources, **kwargs):
103 """Compute the mean ra/dec position of the diaObject given the
104 diaSource locations.
105
106 Parameters
107 ----------
108 diaObjects : `pandas.DataFrame`
109 Summary objects to store values in.
110 diaSources : `pandas.DataFrame` or `pandas.DataFrameGroupBy`
111 Catalog of DiaSources summarized by this DiaObject.
112 **kwargs
113 Any additional keyword arguments that may be passed to the plugin.
114 """
115 for outCol in self.outputColsoutputCols:
116 if outCol not in diaObjects.columns:
117 diaObjects[outCol] = np.nan
118
119 def _computeMeanPos(df):
120 aveCoord = geom.averageSpherePoint(
121 list(geom.SpherePoint(src["ra"], src["decl"], geom.degrees)
122 for idx, src in df.iterrows()))
123 ra = aveCoord.getRa().asDegrees()
124 decl = aveCoord.getDec().asDegrees()
125 if np.isnan(ra) or np.isnan(decl):
126 radecTai = np.nan
127 else:
128 radecTai = df["midPointTai"].max()
129
130 return pd.Series({"ra": aveCoord.getRa().asDegrees(),
131 "decl": aveCoord.getDec().asDegrees(),
132 "radecTai": radecTai})
133
134 ans = diaSources.apply(_computeMeanPos)
135 diaObjects.loc[:, ["ra", "decl", "radecTai"]] = ans
136
137
139
140 htmLevel = pexConfig.Field(
141 dtype=int,
142 doc="Level of the HTM pixelization.",
143 default=20,
144 )
145
146
147@register("ap_HTMIndex")
149 """Compute the mean position of a DiaObject given a set of DiaSources.
150
151 Notes
152 -----
153 This plugin was implemented to satisfy requirements of old APDB interface
154 which required ``pixelId`` column in DiaObject with HTM20 index. APDB
155 interface had migrated to not need that information, but we keep this
156 plugin in case it may be useful for something else.
157 """
158 ConfigClass = HTMIndexDiaPositionConfig
159
160 plugType = 'single'
161
162 inputCols = ["ra", "decl"]
163 outputCols = ["pixelId"]
164 needsFilter = False
165
166 def __init__(self, config, name, metadata):
167 DiaObjectCalculationPlugin.__init__(self, config, name, metadata)
169
170 @classmethod
172 return cls.FLUX_MOMENTS_CALCULATED
173
174 def calculate(self, diaObjects, diaObjectId, **kwargs):
175 """Compute the mean position of a DiaObject given a set of DiaSources
176
177 Parameters
178 ----------
179 diaObjects : `pandas.dataFrame`
180 Summary objects to store values in and read ra/decl from.
181 diaObjectId : `int`
182 Id of the diaObject to update.
183 **kwargs
184 Any additional keyword arguments that may be passed to the plugin.
185 """
186 sphPoint = geom.SpherePoint(
187 diaObjects.at[diaObjectId, "ra"] * geom.degrees,
188 diaObjects.at[diaObjectId, "decl"] * geom.degrees)
189 diaObjects.at[diaObjectId, "pixelId"] = self.pixelator.index(
190 sphPoint.getVector())
191
192
194 pass
195
196
197@register("ap_nDiaSources")
199 """Compute the total number of DiaSources associated with this DiaObject.
200 """
201
202 ConfigClass = NumDiaSourcesDiaPluginConfig
203 outputCols = ["nDiaSources"]
204 plugType = "multi"
205 needsFilter = False
206
207 @classmethod
210
211 def calculate(self, diaObjects, diaSources, **kwargs):
212 """Compute the total number of DiaSources associated with this DiaObject.
213
214 Parameters
215 ----------
216 diaObject : `dict`
217 Summary object to store values in and read ra/decl from.
218 **kwargs
219 Any additional keyword arguments that may be passed to the plugin.
220 """
221 diaObjects.loc[:, "nDiaSources"] = diaSources.diaObjectId.count()
222
223
225 pass
226
227
228@register("ap_diaObjectFlag")
230 """Find if any DiaSource is flagged.
231
232 Set the DiaObject flag if any DiaSource is flagged.
233 """
234
235 ConfigClass = NumDiaSourcesDiaPluginConfig
236 outputCols = ["flags"]
237 plugType = "multi"
238 needsFilter = False
239
240 @classmethod
243
244 def calculate(self, diaObjects, diaSources, **kwargs):
245 """Find if any DiaSource is flagged.
246
247 Set the DiaObject flag if any DiaSource is flagged.
248
249 Parameters
250 ----------
251 diaObject : `dict`
252 Summary object to store values in and read ra/decl from.
253 **kwargs
254 Any additional keyword arguments that may be passed to the plugin.
255 """
256 diaObjects.loc[:, "flags"] = diaSources.flags.any().astype(np.uint64)
257
258
260 pass
261
262
263@register("ap_meanFlux")
265 """Compute the weighted mean and mean error on the point source fluxes
266 of the DiaSource measured on the difference image.
267
268 Additionally store number of usable data points.
269 """
270
271 ConfigClass = WeightedMeanDiaPsFluxConfig
272 outputCols = ["PSFluxMean", "PSFluxMeanErr", "PSFluxNdata"]
273 plugType = "multi"
274 needsFilter = True
275
276 @classmethod
279
280 @catchWarnings(warns=["invalid value encountered",
281 "divide by zero"])
282 def calculate(self,
283 diaObjects,
284 diaSources,
285 filterDiaSources,
286 filterName,
287 **kwargs):
288 """Compute the weighted mean and mean error of the point source flux.
289
290 Parameters
291 ----------
292 diaObject : `dict`
293 Summary object to store values in.
294 diaSources : `pandas.DataFrame`
295 DataFrame representing all diaSources associated with this
296 diaObject.
297 filterDiaSources : `pandas.DataFrame`
298 DataFrame representing diaSources associated with this
299 diaObject that are observed in the band pass ``filterName``.
300 filterName : `str`
301 Simple, string name of the filter for the flux being calculated.
302 **kwargs
303 Any additional keyword arguments that may be passed to the plugin.
304 """
305 meanName = "{}PSFluxMean".format(filterName)
306 errName = "{}PSFluxMeanErr".format(filterName)
307 nDataName = "{}PSFluxNdata".format(filterName)
308 if meanName not in diaObjects.columns:
309 diaObjects[meanName] = np.nan
310 if errName not in diaObjects.columns:
311 diaObjects[errName] = np.nan
312 if nDataName not in diaObjects.columns:
313 diaObjects[nDataName] = 0
314
315 def _weightedMean(df):
316 tmpDf = df[~np.logical_or(np.isnan(df["psFlux"]),
317 np.isnan(df["psFluxErr"]))]
318 tot_weight = np.nansum(1 / tmpDf["psFluxErr"] ** 2)
319 fluxMean = np.nansum(tmpDf["psFlux"]
320 / tmpDf["psFluxErr"] ** 2)
321 fluxMean /= tot_weight
322 if tot_weight > 0:
323 fluxMeanErr = np.sqrt(1 / tot_weight)
324 else:
325 fluxMeanErr = np.nan
326 nFluxData = len(tmpDf)
327
328 return pd.Series({meanName: fluxMean,
329 errName: fluxMeanErr,
330 nDataName: nFluxData},
331 dtype="object")
332
333 diaObjects.loc[:, [meanName, errName, nDataName]] = \
334 filterDiaSources.apply(_weightedMean)
335
336
338 percentiles = pexConfig.ListField(
339 dtype=int,
340 default=[5, 25, 50, 75, 95],
341 doc="Percentiles to calculate to compute values for. Should be "
342 "integer values."
343 )
344
345
346@register("ap_percentileFlux")
348 """Compute percentiles of diaSource fluxes.
349 """
350
351 ConfigClass = PercentileDiaPsFluxConfig
352 # Output columns are created upon instantiation of the class.
353 outputCols = []
354 plugType = "multi"
355 needsFilter = True
356
357 def __init__(self, config, name, metadata, **kwargs):
358 DiaObjectCalculationPlugin.__init__(self,
359 config,
360 name,
361 metadata,
362 **kwargs)
363 self.outputColsoutputColsoutputCols = ["PSFluxPercentile{:02d}".format(percent)
364 for percent in self.config.percentiles]
365
366 @classmethod
369
370 @catchWarnings(warns=["All-NaN slice encountered"])
371 def calculate(self,
372 diaObjects,
373 diaSources,
374 filterDiaSources,
375 filterName,
376 **kwargs):
377 """Compute the percentile fluxes of the point source flux.
378
379 Parameters
380 ----------
381 diaObject : `dict`
382 Summary object to store values in.
383 diaSources : `pandas.DataFrame`
384 DataFrame representing all diaSources associated with this
385 diaObject.
386 filterDiaSources : `pandas.DataFrame`
387 DataFrame representing diaSources associated with this
388 diaObject that are observed in the band pass ``filterName``.
389 filterName : `str`
390 Simple, string name of the filter for the flux being calculated.
391 **kwargs
392 Any additional keyword arguments that may be passed to the plugin.
393 """
394 pTileNames = []
395 for tilePercent in self.config.percentiles:
396 pTileName = "{}PSFluxPercentile{:02d}".format(filterName,
397 tilePercent)
398 pTileNames.append(pTileName)
399 if pTileName not in diaObjects.columns:
400 diaObjects[pTileName] = np.nan
401
402 def _fluxPercentiles(df):
403 pTiles = np.nanpercentile(df["psFlux"], self.config.percentiles)
404 return pd.Series(
405 dict((tileName, pTile)
406 for tileName, pTile in zip(pTileNames, pTiles)))
407
408 diaObjects.loc[:, pTileNames] = filterDiaSources.apply(_fluxPercentiles)
409
410
412 pass
413
414
415@register("ap_sigmaFlux")
417 """Compute scatter of diaSource fluxes.
418 """
419
420 ConfigClass = SigmaDiaPsFluxConfig
421 # Output columns are created upon instantiation of the class.
422 outputCols = ["PSFluxSigma"]
423 plugType = "multi"
424 needsFilter = True
425
426 @classmethod
429
430 def calculate(self,
431 diaObjects,
432 diaSources,
433 filterDiaSources,
434 filterName,
435 **kwargs):
436 """Compute the sigma fluxes of the point source flux.
437
438 Parameters
439 ----------
440 diaObject : `dict`
441 Summary object to store values in.
442 diaSources : `pandas.DataFrame`
443 DataFrame representing all diaSources associated with this
444 diaObject.
445 filterDiaSources : `pandas.DataFrame`
446 DataFrame representing diaSources associated with this
447 diaObject that are observed in the band pass ``filterName``.
448 filterName : `str`
449 Simple, string name of the filter for the flux being calculated.
450 **kwargs
451 Any additional keyword arguments that may be passed to the plugin.
452 """
453 # Set "delta degrees of freedom (ddf)" to 1 to calculate the unbiased
454 # estimator of scatter (i.e. 'N - 1' instead of 'N').
455 diaObjects.loc[:, "{}PSFluxSigma".format(filterName)] = \
456 filterDiaSources.psFlux.std()
457
458
460 pass
461
462
463@register("ap_chi2Flux")
465 """Compute chi2 of diaSource fluxes.
466 """
467
468 ConfigClass = Chi2DiaPsFluxConfig
469
470 # Required input Cols
471 inputCols = ["PSFluxMean"]
472 # Output columns are created upon instantiation of the class.
473 outputCols = ["PSFluxChi2"]
474 plugType = "multi"
475 needsFilter = True
476
477 @classmethod
479 return cls.FLUX_MOMENTS_CALCULATED
480
481 @catchWarnings(warns=["All-NaN slice encountered"])
482 def calculate(self,
483 diaObjects,
484 diaSources,
485 filterDiaSources,
486 filterName,
487 **kwargs):
488 """Compute the chi2 of the point source fluxes.
489
490 Parameters
491 ----------
492 diaObject : `dict`
493 Summary object to store values in.
494 diaSources : `pandas.DataFrame`
495 DataFrame representing all diaSources associated with this
496 diaObject.
497 filterDiaSources : `pandas.DataFrame`
498 DataFrame representing diaSources associated with this
499 diaObject that are observed in the band pass ``filterName``.
500 filterName : `str`
501 Simple, string name of the filter for the flux being calculated.
502 **kwargs
503 Any additional keyword arguments that may be passed to the plugin.
504 """
505 meanName = "{}PSFluxMean".format(filterName)
506
507 def _chi2(df):
508 delta = (df["psFlux"]
509 - diaObjects.at[df.diaObjectId.iat[0], meanName])
510 return np.nansum((delta / df["psFluxErr"]) ** 2)
511
512 diaObjects.loc[:, "{}PSFluxChi2".format(filterName)] = \
513 filterDiaSources.apply(_chi2)
514
515
517 pass
518
519
520@register("ap_madFlux")
522 """Compute median absolute deviation of diaSource fluxes.
523 """
524
525 ConfigClass = MadDiaPsFluxConfig
526
527 # Required input Cols
528 # Output columns are created upon instantiation of the class.
529 outputCols = ["PSFluxMAD"]
530 plugType = "multi"
531 needsFilter = True
532
533 @classmethod
536
537 @catchWarnings(warns=["All-NaN slice encountered"])
538 def calculate(self,
539 diaObjects,
540 diaSources,
541 filterDiaSources,
542 filterName,
543 **kwargs):
544 """Compute the median absolute deviation of the point source fluxes.
545
546 Parameters
547 ----------
548 diaObject : `dict`
549 Summary object to store values in.
550 diaSources : `pandas.DataFrame`
551 DataFrame representing all diaSources associated with this
552 diaObject.
553 filterDiaSources : `pandas.DataFrame`
554 DataFrame representing diaSources associated with this
555 diaObject that are observed in the band pass ``filterName``.
556 filterName : `str`
557 Simple, string name of the filter for the flux being calculated.
558 **kwargs
559 Any additional keyword arguments that may be passed to the plugin.
560 """
561 diaObjects.loc[:, "{}PSFluxMAD".format(filterName)] = \
562 filterDiaSources.psFlux.apply(median_absolute_deviation,
563 ignore_nan=True)
564
565
567 pass
568
569
570@register("ap_skewFlux")
572 """Compute the skew of diaSource fluxes.
573 """
574
575 ConfigClass = SkewDiaPsFluxConfig
576
577 # Required input Cols
578 # Output columns are created upon instantiation of the class.
579 outputCols = ["PSFluxSkew"]
580 plugType = "multi"
581 needsFilter = True
582
583 @classmethod
586
587 def calculate(self,
588 diaObjects,
589 diaSources,
590 filterDiaSources,
591 filterName,
592 **kwargs):
593 """Compute the skew of the point source fluxes.
594
595 Parameters
596 ----------
597 diaObject : `dict`
598 Summary object to store values in.
599 diaSources : `pandas.DataFrame`
600 DataFrame representing all diaSources associated with this
601 diaObject.
602 filterDiaSources : `pandas.DataFrame`
603 DataFrame representing diaSources associated with this
604 diaObject that are observed in the band pass ``filterName``.
605 filterName : `str`
606 Simple, string name of the filter for the flux being calculated.
607 **kwargs
608 Any additional keyword arguments that may be passed to the plugin.
609 """
610 diaObjects.loc[:, "{}PSFluxSkew".format(filterName)] = \
611 filterDiaSources.psFlux.skew()
612
613
615 pass
616
617
618@register("ap_minMaxFlux")
620 """Compute min/max of diaSource fluxes.
621 """
622
623 ConfigClass = MinMaxDiaPsFluxConfig
624
625 # Required input Cols
626 # Output columns are created upon instantiation of the class.
627 outputCols = ["PSFluxMin", "PSFluxMax"]
628 plugType = "multi"
629 needsFilter = True
630
631 @classmethod
634
635 def calculate(self,
636 diaObjects,
637 diaSources,
638 filterDiaSources,
639 filterName,
640 **kwargs):
641 """Compute min/max of the point source fluxes.
642
643 Parameters
644 ----------
645 diaObject : `dict`
646 Summary object to store values in.
647 diaSources : `pandas.DataFrame`
648 DataFrame representing all diaSources associated with this
649 diaObject.
650 filterDiaSources : `pandas.DataFrame`
651 DataFrame representing diaSources associated with this
652 diaObject that are observed in the band pass ``filterName``.
653 filterName : `str`
654 Simple, string name of the filter for the flux being calculated.
655 **kwargs
656 Any additional keyword arguments that may be passed to the plugin.
657 """
658 minName = "{}PSFluxMin".format(filterName)
659 if minName not in diaObjects.columns:
660 diaObjects[minName] = np.nan
661 maxName = "{}PSFluxMax".format(filterName)
662 if maxName not in diaObjects.columns:
663 diaObjects[maxName] = np.nan
664
665 diaObjects.loc[:, minName] = filterDiaSources.psFlux.min()
666 diaObjects.loc[:, maxName] = filterDiaSources.psFlux.max()
667
668
670 pass
671
672
673@register("ap_maxSlopeFlux")
675 """Compute the maximum ratio time ordered deltaFlux / deltaTime.
676 """
677
678 ConfigClass = MinMaxDiaPsFluxConfig
679
680 # Required input Cols
681 # Output columns are created upon instantiation of the class.
682 outputCols = ["PSFluxMaxSlope"]
683 plugType = "multi"
684 needsFilter = True
685
686 @classmethod
689
690 def calculate(self,
691 diaObjects,
692 diaSources,
693 filterDiaSources,
694 filterName,
695 **kwargs):
696 """Compute the maximum ratio time ordered deltaFlux / deltaTime.
697
698 Parameters
699 ----------
700 diaObject : `dict`
701 Summary object to store values in.
702 diaSources : `pandas.DataFrame`
703 DataFrame representing all diaSources associated with this
704 diaObject.
705 filterDiaSources : `pandas.DataFrame`
706 DataFrame representing diaSources associated with this
707 diaObject that are observed in the band pass ``filterName``.
708 filterName : `str`
709 Simple, string name of the filter for the flux being calculated.
710 **kwargs
711 Any additional keyword arguments that may be passed to the plugin.
712 """
713
714 def _maxSlope(df):
715 tmpDf = df[~np.logical_or(np.isnan(df["psFlux"]),
716 np.isnan(df["midPointTai"]))]
717 if len(tmpDf) < 2:
718 return np.nan
719 times = tmpDf["midPointTai"].to_numpy()
720 timeArgs = times.argsort()
721 times = times[timeArgs]
722 fluxes = tmpDf["psFlux"].to_numpy()[timeArgs]
723 return (np.diff(fluxes) / np.diff(times)).max()
724
725 diaObjects.loc[:, "{}PSFluxMaxSlope".format(filterName)] = \
726 filterDiaSources.apply(_maxSlope)
727
728
730 pass
731
732
733@register("ap_meanErrFlux")
735 """Compute the mean of the dia source errors.
736 """
737
738 ConfigClass = ErrMeanDiaPsFluxConfig
739
740 # Required input Cols
741 # Output columns are created upon instantiation of the class.
742 outputCols = ["PSFluxErrMean"]
743 plugType = "multi"
744 needsFilter = True
745
746 @classmethod
749
750 def calculate(self,
751 diaObjects,
752 diaSources,
753 filterDiaSources,
754 filterName,
755 **kwargs):
756 """Compute the mean of the dia source errors.
757
758 Parameters
759 ----------
760 diaObject : `dict`
761 Summary object to store values in.
762 diaSources : `pandas.DataFrame`
763 DataFrame representing all diaSources associated with this
764 diaObject.
765 filterDiaSources : `pandas.DataFrame`
766 DataFrame representing diaSources associated with this
767 diaObject that are observed in the band pass ``filterName``.
768 filterName : `str`
769 Simple, string name of the filter for the flux being calculated.
770 **kwargs
771 Any additional keyword arguments that may be passed to the plugin.
772 """
773 diaObjects.loc[:, "{}PSFluxErrMean".format(filterName)] = \
774 filterDiaSources.psFluxErr.mean()
775
776
778 pass
779
780
781@register("ap_linearFit")
783 """Compute fit a linear model to flux vs time.
784 """
785
786 ConfigClass = LinearFitDiaPsFluxConfig
787
788 # Required input Cols
789 # Output columns are created upon instantiation of the class.
790 outputCols = ["PSFluxLinearSlope", "PSFluxLinearIntercept"]
791 plugType = "multi"
792 needsFilter = True
793
794 @classmethod
797
798 def calculate(self,
799 diaObjects,
800 diaSources,
801 filterDiaSources,
802 filterName,
803 **kwargs):
804 """Compute fit a linear model to flux vs time.
805
806 Parameters
807 ----------
808 diaObject : `dict`
809 Summary object to store values in.
810 diaSources : `pandas.DataFrame`
811 DataFrame representing all diaSources associated with this
812 diaObject.
813 filterDiaSources : `pandas.DataFrame`
814 DataFrame representing diaSources associated with this
815 diaObject that are observed in the band pass ``filterName``.
816 filterName : `str`
817 Simple, string name of the filter for the flux being calculated.
818 **kwargs
819 Any additional keyword arguments that may be passed to the plugin.
820 """
821
822 mName = "{}PSFluxLinearSlope".format(filterName)
823 if mName not in diaObjects.columns:
824 diaObjects[mName] = np.nan
825 bName = "{}PSFluxLinearIntercept".format(filterName)
826 if bName not in diaObjects.columns:
827 diaObjects[bName] = np.nan
828
829 def _linearFit(df):
830 tmpDf = df[~np.logical_or(
831 np.isnan(df["psFlux"]),
832 np.logical_or(np.isnan(df["psFluxErr"]),
833 np.isnan(df["midPointTai"])))]
834 if len(tmpDf) < 2:
835 return pd.Series({mName: np.nan, bName: np.nan})
836 fluxes = tmpDf["psFlux"].to_numpy()
837 errors = tmpDf["psFluxErr"].to_numpy()
838 times = tmpDf["midPointTai"].to_numpy()
839 A = np.array([times / errors, 1 / errors]).transpose()
840 m, b = lsq_linear(A, fluxes / errors).x
841 return pd.Series({mName: m, bName: b})
842
843 diaObjects.loc[:, [mName, bName]] = filterDiaSources.apply(_linearFit)
844
845
847 pass
848
849
850@register("ap_stetsonJ")
852 """Compute the StetsonJ statistic on the DIA point source fluxes.
853 """
854
855 ConfigClass = LinearFitDiaPsFluxConfig
856
857 # Required input Cols
858 inputCols = ["PSFluxMean"]
859 # Output columns are created upon instantiation of the class.
860 outputCols = ["PSFluxStetsonJ"]
861 plugType = "multi"
862 needsFilter = True
863
864 @classmethod
866 return cls.FLUX_MOMENTS_CALCULATED
867
868 def calculate(self,
869 diaObjects,
870 diaSources,
871 filterDiaSources,
872 filterName,
873 **kwargs):
874 """Compute the StetsonJ statistic on the DIA point source fluxes.
875
876 Parameters
877 ----------
878 diaObject : `dict`
879 Summary object to store values in.
880 diaSources : `pandas.DataFrame`
881 DataFrame representing all diaSources associated with this
882 diaObject.
883 filterDiaSources : `pandas.DataFrame`
884 DataFrame representing diaSources associated with this
885 diaObject that are observed in the band pass ``filterName``.
886 filterName : `str`
887 Simple, string name of the filter for the flux being calculated.
888 **kwargs
889 Any additional keyword arguments that may be passed to the plugin.
890 """
891 meanName = "{}PSFluxMean".format(filterName)
892
893 def _stetsonJ(df):
894 tmpDf = df[~np.logical_or(np.isnan(df["psFlux"]),
895 np.isnan(df["psFluxErr"]))]
896 if len(tmpDf) < 2:
897 return np.nan
898 fluxes = tmpDf["psFlux"].to_numpy()
899 errors = tmpDf["psFluxErr"].to_numpy()
900
901 return self._stetson_J(
902 fluxes,
903 errors,
904 diaObjects.at[tmpDf.diaObjectId.iat[0], meanName])
905
906 diaObjects.loc[:, "{}PSFluxStetsonJ".format(filterName)] = \
907 filterDiaSources.apply(_stetsonJ)
908
909 def _stetson_J(self, fluxes, errors, mean=None):
910 """Compute the single band stetsonJ statistic.
911
912 Parameters
913 ----------
914 fluxes : `numpy.ndarray` (N,)
915 Calibrated lightcurve flux values.
916 errors : `numpy.ndarray` (N,)
917 Errors on the calibrated lightcurve fluxes.
918 mean : `float`
919 Starting mean from previous plugin.
920
921 Returns
922 -------
923 stetsonJ : `float`
924 stetsonJ statistic for the input fluxes and errors.
925
926 References
927 ----------
928 .. [1] Stetson, P. B., "On the Automatic Determination of Light-Curve
929 Parameters for Cepheid Variables", PASP, 108, 851S, 1996
930 """
931 n_points = len(fluxes)
932 flux_mean = self._stetson_mean(fluxes, errors, mean)
933 delta_val = (
934 np.sqrt(n_points / (n_points - 1)) * (fluxes - flux_mean) / errors)
935 p_k = delta_val ** 2 - 1
936
937 return np.mean(np.sign(p_k) * np.sqrt(np.fabs(p_k)))
938
939 def _stetson_mean(self,
940 values,
941 errors,
942 mean=None,
943 alpha=2.,
944 beta=2.,
945 n_iter=20,
946 tol=1e-6):
947 """Compute the stetson mean of the fluxes which down-weights outliers.
948
949 Weighted biased on an error weighted difference scaled by a constant
950 (1/``a``) and raised to the power beta. Higher betas more harshly
951 penalize outliers and ``a`` sets the number of sigma where a weighted
952 difference of 1 occurs.
953
954 Parameters
955 ----------
956 values : `numpy.dnarray`, (N,)
957 Input values to compute the mean of.
958 errors : `numpy.ndarray`, (N,)
959 Errors on the input values.
960 mean : `float`
961 Starting mean value or None.
962 alpha : `float`
963 Scalar down-weighting of the fractional difference. lower->more
964 clipping. (Default value is 2.)
965 beta : `float`
966 Power law slope of the used to down-weight outliers. higher->more
967 clipping. (Default value is 2.)
968 n_iter : `int`
969 Number of iterations of clipping.
970 tol : `float`
971 Fractional and absolute tolerance goal on the change in the mean
972 before exiting early. (Default value is 1e-6)
973
974 Returns
975 -------
976 mean : `float`
977 Weighted stetson mean result.
978
979 References
980 ----------
981 .. [1] Stetson, P. B., "On the Automatic Determination of Light-Curve
982 Parameters for Cepheid Variables", PASP, 108, 851S, 1996
983 """
984 n_points = len(values)
985 n_factor = np.sqrt(n_points / (n_points - 1))
986 inv_var = 1 / errors ** 2
987
988 if mean is None:
989 mean = np.average(values, weights=inv_var)
990 for iter_idx in range(n_iter):
991 chi = np.fabs(n_factor * (values - mean) / errors)
992 tmp_mean = np.average(
993 values,
994 weights=inv_var / (1 + (chi / alpha) ** beta))
995 diff = np.fabs(tmp_mean - mean)
996 mean = tmp_mean
997 if diff / mean < tol and diff < tol:
998 break
999 return mean
1000
1001
1003 pass
1004
1005
1006@register("ap_meanTotFlux")
1008 """Compute the weighted mean and mean error on the point source fluxes
1009 forced photometered at the DiaSource location in the calibrated image.
1010
1011 Additionally store number of usable data points.
1012 """
1013
1014 ConfigClass = WeightedMeanDiaPsFluxConfig
1015 outputCols = ["TOTFluxMean", "TOTFluxMeanErr"]
1016 plugType = "multi"
1017 needsFilter = True
1018
1019 @classmethod
1022
1023 @catchWarnings(warns=["invalid value encountered",
1024 "divide by zero"])
1025 def calculate(self,
1026 diaObjects,
1027 diaSources,
1028 filterDiaSources,
1029 filterName,
1030 **kwargs):
1031 """Compute the weighted mean and mean error of the point source flux.
1032
1033 Parameters
1034 ----------
1035 diaObject : `dict`
1036 Summary object to store values in.
1037 diaSources : `pandas.DataFrame`
1038 DataFrame representing all diaSources associated with this
1039 diaObject.
1040 filterDiaSources : `pandas.DataFrame`
1041 DataFrame representing diaSources associated with this
1042 diaObject that are observed in the band pass ``filterName``.
1043 filterName : `str`
1044 Simple, string name of the filter for the flux being calculated.
1045 **kwargs
1046 Any additional keyword arguments that may be passed to the plugin.
1047 """
1048 totMeanName = "{}TOTFluxMean".format(filterName)
1049 if totMeanName not in diaObjects.columns:
1050 diaObjects[totMeanName] = np.nan
1051 totErrName = "{}TOTFluxMeanErr".format(filterName)
1052 if totErrName not in diaObjects.columns:
1053 diaObjects[totErrName] = np.nan
1054
1055 def _meanFlux(df):
1056 tmpDf = df[~np.logical_or(np.isnan(df["totFlux"]),
1057 np.isnan(df["totFluxErr"]))]
1058 tot_weight = np.nansum(1 / tmpDf["totFluxErr"] ** 2)
1059 fluxMean = np.nansum(tmpDf["totFlux"]
1060 / tmpDf["totFluxErr"] ** 2)
1061 fluxMean /= tot_weight
1062 fluxMeanErr = np.sqrt(1 / tot_weight)
1063
1064 return pd.Series({totMeanName: fluxMean,
1065 totErrName: fluxMeanErr})
1066
1067 diaObjects.loc[:, [totMeanName, totErrName]] = \
1068 filterDiaSources.apply(_meanFlux)
1069
1070
1072 pass
1073
1074
1075@register("ap_sigmaTotFlux")
1077 """Compute scatter of diaSource fluxes.
1078 """
1079
1080 ConfigClass = SigmaDiaPsFluxConfig
1081 # Output columns are created upon instantiation of the class.
1082 outputCols = ["TOTFluxSigma"]
1083 plugType = "multi"
1084 needsFilter = True
1085
1086 @classmethod
1089
1090 def calculate(self,
1091 diaObjects,
1092 diaSources,
1093 filterDiaSources,
1094 filterName,
1095 **kwargs):
1096 """Compute the sigma fluxes of the point source flux measured on the
1097 calibrated image.
1098
1099 Parameters
1100 ----------
1101 diaObject : `dict`
1102 Summary object to store values in.
1103 diaSources : `pandas.DataFrame`
1104 DataFrame representing all diaSources associated with this
1105 diaObject.
1106 filterDiaSources : `pandas.DataFrame`
1107 DataFrame representing diaSources associated with this
1108 diaObject that are observed in the band pass ``filterName``.
1109 filterName : `str`
1110 Simple, string name of the filter for the flux being calculated.
1111 **kwargs
1112 Any additional keyword arguments that may be passed to the plugin.
1113 """
1114 # Set "delta degrees of freedom (ddf)" to 1 to calculate the unbiased
1115 # estimator of scatter (i.e. 'N - 1' instead of 'N').
1116 diaObjects.loc[:, "{}TOTFluxSigma".format(filterName)] = \
1117 filterDiaSources.totFlux.std()
int max
Point in an unspecified spherical coordinate system.
Definition: SpherePoint.h:57
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaObjectId, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def __init__(self, config, name, metadata, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def _stetson_mean(self, values, errors, mean=None, alpha=2., beta=2., n_iter=20, tol=1e-6)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
def calculate(self, diaObjects, diaSources, filterDiaSources, filterName, **kwargs)
HtmPixelization provides HTM indexing of points and regions.
daf::base::PropertyList * list
Definition: fits.cc:928
SpherePoint averageSpherePoint(std::vector< SpherePoint > const &coords)
Return the average of a list of coordinates.
Definition: SpherePoint.cc:235