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

69 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-12-06 02:44 -0800

1from __future__ import annotations 

2 

3import operator 

4from typing import cast 

5 

6import numpy as np 

7from lsst.pex.config import ChoiceField, Field 

8 

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

10from ...statistics import nansigmaMad 

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 nansigmaMad( 

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

59 ) 

60 ), 

61 ) 

62 

63 

64class CountAction(ScalarAction): 

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

66 

67 def getInputSchema(self) -> KeyedDataSchema: 

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

69 

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

71 mask = self.getMask(**kwargs) 

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

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

74 return cast(Scalar, len(arr)) 

75 

76 

77class ApproxFloor(ScalarAction): 

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

79 

80 def getInputSchema(self) -> KeyedDataSchema: 

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

82 

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

84 mask = self.getMask(**kwargs) 

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

86 x = len(value) // 10 

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

88 

89 

90class FracThreshold(ScalarAction): 

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

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

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

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

95 use op="le". 

96 """ 

97 

98 op = ChoiceField[str]( 

99 doc="Operator name string.", 

100 allowed={ 

101 "lt": "less than threshold", 

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

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

104 "gt": "greater than threshold", 

105 }, 

106 ) 

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

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

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

110 

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

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

113 

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

115 mask = self.getMask(**kwargs) 

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

117 values = values[mask] # type: ignore 

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

119 result = cast( 

120 Scalar, 

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

122 ) 

123 if self.percent: 

124 return 100.0 * result 

125 else: 

126 return result