Coverage for python/lsst/analysis/tools/actions/scalar/scalarActions.py: 61%

69 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-08-20 02:32 -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 cast(Scalar, float(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 cast(Scalar, float(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 cast(Scalar, float(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 cast( 

55 Scalar, 

56 float( 

57 sps.median_abs_deviation( 

58 data[self.vectorKey.format(**kwargs)][mask], # type: ignore 

59 scale="normal", 

60 nan_policy="omit", 

61 ) 

62 ), 

63 ) 

64 

65 

66class CountAction(ScalarAction): 

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

68 

69 def getInputSchema(self) -> KeyedDataSchema: 

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

71 

72 def __call__(self, data: KeyedData, **kwargs) -> Scalar: 

73 mask = self.getMask(**kwargs) 

74 arr = cast(Vector, data[self.vectorKey.format(**kwargs)])[mask] 

75 arr = arr[~np.isnan(arr)] 

76 return cast(Scalar, len(arr)) 

77 

78 

79class ApproxFloor(ScalarAction): 

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

81 

82 def getInputSchema(self) -> KeyedDataSchema: 

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

84 

85 def __call__(self, data: KeyedData, **kwargs) -> Scalar: 

86 mask = self.getMask(**kwargs) 

87 value = np.sort(data[self.vectorKey.format(**kwargs)][mask]) # type: ignore 

88 x = len(value) // 10 

89 return cast(Scalar, float(np.nanmedian(value[-x:]))) 

90 

91 

92class FracThreshold(ScalarAction): 

93 """Compute the fraction of a distribution that is above or below a 

94 specified threshold. The operator is specified as a string, for example, 

95 "lt", "le", "ge", "gt" for the mathematical operations <, <=, >=, >. To 

96 compute the fraction of elements with values less than a given threshold, 

97 use op="le". 

98 """ 

99 

100 op = ChoiceField[str]( 

101 doc="Operator name string.", 

102 allowed={ 

103 "lt": "less than threshold", 

104 "le": "less than or equal to threshold", 

105 "ge": "greater than or equal to threshold", 

106 "gt": "greater than threshold", 

107 }, 

108 ) 

109 threshold = Field[float](doc="Threshold to apply.") 

110 vectorKey = Field[str](doc="Name of column") 

111 percent = Field[bool](doc="Express result as percentage", default=False) 

112 

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

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

115 

116 def __call__(self, data: KeyedData, **kwargs) -> Scalar: 

117 mask = self.getMask(**kwargs) 

118 values = data[self.vectorKey.format(**kwargs)] 

119 values = values[mask] # type: ignore 

120 values = values[np.logical_not(np.isnan(values))] 

121 result = cast( 

122 Scalar, 

123 float(np.sum(getattr(operator, self.op)(values, self.threshold)) / len(values)), # type: ignore 

124 ) 

125 if self.percent: 

126 return 100.0 * result 

127 else: 

128 return result