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

1from __future__ import annotations 

2 

3import operator 

4from typing import cast 

5 

6import numpy as np 

7import scipy.stats as sps 

8from lsst.pex.config import ChoiceField, Field 

9 

10from ...interfaces import KeyedData, KeyedDataSchema, Scalar, ScalarAction, Vector 

11 

12 

13class MedianAction(ScalarAction): 

14 vectorKey = Field[str]("Key of Vector to median") 

15 

16 def getInputSchema(self) -> KeyedDataSchema: 

17 return ((self.vectorKey, Vector),) 

18 

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]) 

22 

23 

24class MeanAction(ScalarAction): 

25 vectorKey = Field[str]("Key of Vector from which to calculate mean") 

26 

27 def getInputSchema(self) -> KeyedDataSchema: 

28 return ((self.vectorKey, Vector),) 

29 

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]) 

33 

34 

35class StdevAction(ScalarAction): 

36 vectorKey = Field[str]("Key of Vector from which to calculate std deviation") 

37 

38 def getInputSchema(self) -> KeyedDataSchema: 

39 return ((self.vectorKey, Vector),) 

40 

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]) 

44 

45 

46class SigmaMadAction(ScalarAction): 

47 vectorKey = Field[str]("Key of Vector to median") 

48 

49 def getInputSchema(self) -> KeyedDataSchema: 

50 return ((self.vectorKey, Vector),) 

51 

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 ) 

59 

60 

61class CountAction(ScalarAction): 

62 vectorKey = Field[str]("Key of Vector to median") 

63 

64 def getInputSchema(self) -> KeyedDataSchema: 

65 return ((self.vectorKey, Vector),) 

66 

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 

72 

73 

74class ApproxFloor(ScalarAction): 

75 vectorKey = Field[str](doc="Key for the vector to perform action on", optional=False) 

76 

77 def getInputSchema(self) -> KeyedDataSchema: 

78 return ((self.vectorKey, Vector),) 

79 

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:]) 

85 

86 

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 """ 

94 

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) 

107 

108 def getInputSchema(self, **kwargs) -> KeyedDataSchema: 

109 return ((self.vectorKey.format(**kwargs), Vector),) 

110 

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