Coverage for python/lsst/analysis/tools/actions/scalar/scalarActions.py: 61%
69 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-05 01:25 -0700
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-05 01:25 -0700
1from __future__ import annotations
3import operator
4from typing import cast
6import numpy as np
7import scipy.stats as sps
8from lsst.pex.config import ChoiceField, Field
10from ...interfaces import KeyedData, KeyedDataSchema, Scalar, ScalarAction, Vector
13class MedianAction(ScalarAction):
14 vectorKey = Field[str]("Key of Vector to median")
16 def getInputSchema(self) -> KeyedDataSchema:
17 return ((self.vectorKey, Vector),)
19 def __call__(self, data: KeyedData, **kwargs) -> Scalar:
20 mask = self.getMask(**kwargs)
21 return np.nanmedian(cast(Vector, data[self.vectorKey.format(**kwargs)])[mask])
24class MeanAction(ScalarAction):
25 vectorKey = Field[str]("Key of Vector from which to calculate mean")
27 def getInputSchema(self) -> KeyedDataSchema:
28 return ((self.vectorKey, Vector),)
30 def __call__(self, data: KeyedData, **kwargs) -> Scalar:
31 mask = self.getMask(**kwargs)
32 return np.nanmean(cast(Vector, data[self.vectorKey.format(**kwargs)])[mask])
35class StdevAction(ScalarAction):
36 vectorKey = Field[str]("Key of Vector from which to calculate std deviation")
38 def getInputSchema(self) -> KeyedDataSchema:
39 return ((self.vectorKey, Vector),)
41 def __call__(self, data: KeyedData, **kwargs) -> Scalar:
42 mask = self.getMask(**kwargs)
43 return np.nanstd(cast(Vector, data[self.vectorKey.format(**kwargs)])[mask])
46class SigmaMadAction(ScalarAction):
47 vectorKey = Field[str]("Key of Vector to median")
49 def getInputSchema(self) -> KeyedDataSchema:
50 return ((self.vectorKey, Vector),)
52 def __call__(self, data: KeyedData, **kwargs) -> Scalar:
53 mask = self.getMask(**kwargs)
54 return sps.median_abs_deviation(
55 cast(Vector, data[self.vectorKey.format(**kwargs)])[mask],
56 scale="normal", # type: ignore
57 nan_policy="omit",
58 )
61class CountAction(ScalarAction):
62 vectorKey = Field[str]("Key of Vector to median")
64 def getInputSchema(self) -> KeyedDataSchema:
65 return ((self.vectorKey, Vector),)
67 def __call__(self, data: KeyedData, **kwargs) -> Scalar:
68 mask = self.getMask(**kwargs)
69 arr = cast(Vector, data[self.vectorKey.format(**kwargs)])[mask]
70 arr = arr[~np.isnan(arr)]
71 return len(arr) # type: ignore
74class ApproxFloor(ScalarAction):
75 vectorKey = Field[str](doc="Key for the vector to perform action on", optional=False)
77 def getInputSchema(self) -> KeyedDataSchema:
78 return ((self.vectorKey, Vector),)
80 def __call__(self, data: KeyedData, **kwargs) -> Scalar:
81 mask = self.getMask(**kwargs)
82 value = np.sort(data[self.vectorKey.format(**kwargs)][mask]) # type: ignore
83 x = len(value) // 10
84 return np.nanmedian(value[-x:])
87class FracThreshold(ScalarAction):
88 """Compute the fraction of a distribution that is above or below a
89 specified threshold. The operator is specified as a string, for example,
90 "lt", "le", "ge", "gt" for the mathematical operations <, <=, >=, >. To
91 compute the fraction of elements with values less than a given threshold,
92 use op="le".
93 """
95 op = ChoiceField[str](
96 doc="Operator name string.",
97 allowed={
98 "lt": "less than threshold",
99 "le": "less than or equal to threshold",
100 "ge": "greater than or equal to threshold",
101 "gt": "greater than threshold",
102 },
103 )
104 threshold = Field[float](doc="Threshold to apply.")
105 vectorKey = Field[str](doc="Name of column")
106 percent = Field[bool](doc="Express result as percentage", default=False)
108 def getInputSchema(self, **kwargs) -> KeyedDataSchema:
109 return ((self.vectorKey.format(**kwargs), Vector),)
111 def __call__(self, data: KeyedData, **kwargs) -> Scalar:
112 mask = self.getMask(**kwargs)
113 values = data[self.vectorKey.format(**kwargs)]
114 values = values[mask] # type: ignore
115 values = values[np.logical_not(np.isnan(values))]
116 result = np.sum(getattr(operator, self.op)(values, self.threshold)) / len(values) # type: ignore
117 if self.percent:
118 return 100.0 * result
119 else:
120 return result