Coverage for python/lsst/pipe/tasks/dataFrameActions/_actions.py: 61%

88 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-15 10:35 +0000

1from __future__ import annotations 

2 

3__all__ = ("SingleColumnAction", "MultiColumnAction", "CoordColumn", "MagColumnDN", "SumColumns", "AddColumn", 

4 "DivideColumns", "SubtractColumns", "MultiplyColumns", "FractionalDifferenceColumns", 

5 "MagColumnNanoJansky", "DiffOfDividedColumns", "PercentDiffOfDividedColumns",) 

6 

7from typing import Iterable 

8 

9import warnings 

10import numpy as np 

11import pandas as pd 

12from astropy import units 

13 

14from lsst.pex.config.configurableActions import ConfigurableActionStructField, ConfigurableActionField 

15from ._baseDataFrameActions import DataFrameAction 

16from ._evalColumnExpression import makeColumnExpressionAction 

17 

18from lsst.pex.config import Field 

19 

20 

21class SingleColumnAction(DataFrameAction): 

22 column = Field(doc="Column to load for this action", dtype=str, optional=False) 

23 

24 @property 

25 def columns(self) -> Iterable[str]: 

26 return (self.column, ) 

27 

28 def __call__(self, df, **kwargs): 

29 return df[self.column] 

30 

31 

32class MultiColumnAction(DataFrameAction): 

33 actions = ConfigurableActionStructField(doc="Configurable actions to use in a joint action") 

34 

35 @property 

36 def columns(self) -> Iterable[str]: 

37 yield from (column for action in self.actions for column in action.columns) 

38 

39 

40class CoordColumn(SingleColumnAction): 

41 inRadians = Field(doc="Return the column in radians if true", default=True, dtype=bool) 

42 

43 def __call__(self, df): 

44 col = super().__call__(df) 

45 return col * 180 / np.pi if self.inRadians else col 

46 

47 

48class MagColumnDN(SingleColumnAction): 

49 coadd_zeropoint = Field(doc="Magnitude zero point", dtype=float, default=27) 

50 

51 def __call__(self, df: pd.DataFrame, **kwargs): 

52 if not (fluxMag0 := kwargs.get('fluxMag0')): 

53 fluxMag0 = 1/np.power(10, -0.4*self.coadd_zeropoint) 

54 

55 with warnings.catch_warnings(): 

56 warnings.filterwarnings('ignore', r'invalid value encountered') 

57 warnings.filterwarnings('ignore', r'divide by zero') 

58 return -2.5 * np.log10(df[self.column] / fluxMag0) 

59 

60 

61class MagColumnNanoJansky(SingleColumnAction): 

62 

63 def __call__(self, df: pd.DataFrame, **kwargs): 

64 

65 with warnings.catch_warnings(): 

66 warnings.filterwarnings('ignore', r'invalid value encountered') 

67 warnings.filterwarnings('ignore', r'divide by zero') 

68 return -2.5 * np.log10((df[self.column] * 1e-9) / 3631.0) 

69 

70 

71class NanoJansky(SingleColumnAction): 

72 ab_flux_scale = Field(doc="Scaling of ab flux", dtype=float, default=(0*units.ABmag).to_value(units.nJy)) 

73 coadd_zeropoint = Field(doc="Magnitude zero point", dtype=float, default=27) 

74 

75 def __call__(self, df, **kwargs): 

76 dataNumber = super().__call__(df, **kwargs) 

77 if not (fluxMag0 := kwargs.get('fluxMag0')): 

78 fluxMag0 = 1/np.power(10, -0.4*self.coadd_zeropoint) 

79 return self.ab_flux_scale * dataNumber / fluxMag0 

80 

81 def setDefaults(self): 

82 super().setDefaults() 

83 self.cache = True # cache this action for future calls 

84 

85 

86class NanoJanskyErr(SingleColumnAction): 

87 flux_mag_err = Field(doc="Error in the magnitude zeropoint", dtype=float, default=0) 

88 flux_action = ConfigurableActionField(doc="Action to use if flux is not provided to the call method", 

89 default=NanoJansky, dtype=DataFrameAction) 

90 

91 @property 

92 def columns(self): 

93 yield from zip((self.column,), self.flux_action.columns) 

94 

95 def __call__(self, df, flux_column=None, flux_mag_err=None, **kwargs): 

96 if flux_column is None: 

97 flux_column = self.flux_action(df, **kwargs) 

98 if flux_mag_err is None: 

99 flux_mag_err = self.flux_mag_err 

100 

101 

102_docs = """This is a `DataFrameAction` that is designed to add two columns 

103together and return the result. 

104""" 

105SumColumns = makeColumnExpressionAction("SumColumns", "colA+colB", 

106 exprDefaults={"colA": SingleColumnAction, 

107 "colB": SingleColumnAction}, 

108 docstring=_docs) 

109 

110_docs = """This is a `MultiColumnAction` that is designed to subtract two columns 

111together and return the result. 

112""" 

113SubtractColumns = makeColumnExpressionAction("SubtractColumns", "colA-colB", 

114 exprDefaults={"colA": SingleColumnAction, 

115 "colB": SingleColumnAction}, 

116 docstring=_docs) 

117 

118_docs = """This is a `MultiColumnAction` that is designed to multiply two columns 

119together and return the result. 

120""" 

121MultiplyColumns = makeColumnExpressionAction("MultiplyColumns", "colA*colB", 

122 exprDefaults={"colA": SingleColumnAction, 

123 "colB": SingleColumnAction}, 

124 docstring=_docs) 

125 

126_docs = """This is a `MultiColumnAction` that is designed to divide two columns 

127together and return the result. 

128""" 

129DivideColumns = makeColumnExpressionAction("DivideColumns", "colA/colB", 

130 exprDefaults={"colA": SingleColumnAction, 

131 "colB": SingleColumnAction}, 

132 docstring=_docs) 

133 

134_docs = """This is a `MultiColumnAction` that is designed to divide two columns 

135together, subtract one and return the result. 

136""" 

137FractionalDifferenceColumns = makeColumnExpressionAction("FractionalDifferenceColumns", "(colA-colB)/colB", 

138 exprDefaults={"colA": SingleColumnAction, 

139 "colB": SingleColumnAction}, 

140 docstring=_docs) 

141 

142_docs = """This is a `MultiColumnAction` that is designed to subtract the division of two columns 

143from the division of two other columns and return the result (i.e. colA1/colB1 - colA2/colB2). 

144""" 

145DiffOfDividedColumns = makeColumnExpressionAction("DiffOfDividedColumns", "(colA1/colB1)-(colA2/colB2)", 

146 exprDefaults={"colA1": SingleColumnAction, 

147 "colB1": SingleColumnAction, 

148 "colA2": SingleColumnAction, 

149 "colB2": SingleColumnAction}, 

150 docstring=_docs) 

151_docs = """This is a `MultiColumnAction` that is designed to compute the percent difference 

152between the division of two columns and the division of two other columns and return the result 

153(i.e. 100*((colA1/colB1 - colA2/colB2)/(colA1/colB1))). 

154""" 

155PercentDiffOfDividedColumns = makeColumnExpressionAction("PercentDiffOfDividedColumns", 

156 "100*(((colA1/colB1)-(colA2/colB2))/(colA1/colB1))", 

157 exprDefaults={"colA1": SingleColumnAction, 

158 "colB1": SingleColumnAction, 

159 "colA2": SingleColumnAction, 

160 "colB2": SingleColumnAction}, 

161 docstring=_docs) 

162 

163 

164class AddColumn(DataFrameAction): 

165 aggregator = ConfigurableActionField(doc="This is an instance of a Dataframe action that will be used " 

166 "to create a new column", dtype=DataFrameAction) 

167 newColumn = Field(doc="Name of the new column to add", dtype=str) 

168 

169 @property 

170 def columns(self) -> Iterable[str]: 

171 yield from self.aggregator.columns 

172 

173 def __call__(self, df, **kwargs) -> pd.DataFrame: 

174 # do your calculation and and 

175 df[self.newColumn] = self.aggregator(df, kwargs) 

176 return df