Coverage for ebcpy/utils/statistics_analyzer.py: 86%

51 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-09-19 12:21 +0000

1"""Module for calculating statistical 

2measures based on given methods.""" 

3 

4import numpy as np 

5import sklearn.metrics as skmetrics 

6 

7 

8class StatisticsAnalyzer: 

9 """Class for calculation of the statistical measure based on the 

10 given method. Either instantiate the class and run 

11 StatisticsAnalyzer.calc(meas, sim), or go for direct calculation with 

12 StatisticsAnalyzer.calc_METHOD(meas, sim). Where METHOD stands for one 

13 of the available methods (see below). 

14 

15 :param (str, callable) method: 

16 If string, it must be one of the following: 

17 - MAE(Mean absolute error) 

18 - R2(coefficient of determination) 

19 - MSE (Mean squared error) 

20 - RMSE(root mean square error) 

21 - CVRMSE(variance of RMSE) 

22 - NRMSE(Normalized RMSE) 

23 

24 If callable, the function needs to take 

25 exactly two arguments and return a scalar value (e.g. float). 

26 The arguments should be able to handle list and numpy arrays. 

27 

28 Example: 

29 

30 >>> def my_func(x, y) 

31 >>> return sum(x - y) 

32 >>> StatisticsAnalyzer(method=my_func) 

33 

34 :param Boolean for_minimization: 

35 Default True. To reduce (minimize) the error in given data, 

36 we either have to minimize or maximize the statistical measure. 

37 Example: R2 has to be maximized to minimize the error in data. 

38 """ 

39 

40 def __init__(self, method, for_minimization=True): 

41 """Instantiate class parameters""" 

42 _supported_methods = {"mae": self.calc_mae, 

43 "r2": self.calc_r2, 

44 "mse": self.calc_mse, 

45 "rmse": self.calc_rmse, 

46 "cvrmse": self.calc_cvrmse, 

47 "nmbe": self.calc_nmbe, 

48 "nrmse": self.calc_nrmse, 

49 "user-function": None} 

50 

51 _minimization_factors = {"mae": 1, 

52 "r2": -1, 

53 "mse": 1, 

54 "rmse": 1, 

55 "cvrmse": 1, 

56 "nmbe": 1, 

57 "nrmse": 1, 

58 "user-function": 1} 

59 

60 # Check if method is function or string 

61 if callable(method): 

62 _method_internal = "user-function" 

63 _supported_methods[_method_internal] = method 

64 elif isinstance(method, str): 

65 # Remove case-sensitive input 

66 _method_internal = method.lower() 

67 else: 

68 raise TypeError( 

69 f"Given method is of type {type(method)} but should be " 

70 f"either string or function.") 

71 

72 if _method_internal not in _supported_methods: 

73 raise ValueError(f"The given method {_method_internal} is not supported.\n " 

74 f"Choose one out of: {', '.join(_supported_methods.keys())}") 

75 self._calc_internal = _supported_methods[_method_internal] 

76 self.for_minimization = for_minimization 

77 self._min_fac = _minimization_factors[_method_internal] 

78 

79 def calc(self, meas, sim): 

80 """Placeholder class before instantiating the class correctly.""" 

81 if self.for_minimization: 

82 return self._calc_internal(meas, sim) * self._min_fac 

83 return self._calc_internal(meas, sim) 

84 

85 @staticmethod 

86 def calc_mae(meas, sim): 

87 """ 

88 Calculates the MAE (mean absolute error) 

89 for the given numpy array of measured and simulated data. 

90 

91 :param np.array meas: 

92 Array with measurement data 

93 :param np.array sim: 

94 Array with simulation data 

95 :return: float MAE: 

96 MAE os the given data. 

97 """ 

98 return skmetrics.mean_absolute_error(meas, sim) 

99 

100 @staticmethod 

101 def calc_r2(meas, sim): 

102 """ 

103 Calculates the MAE (mean absolute error) 

104 for the given numpy array of measured and simulated data. 

105 

106 :param np.array meas: 

107 Array with measurement data 

108 :param np.array sim: 

109 Array with simulation data 

110 :return: float MAE: 

111 R2 of the given data. 

112 """ 

113 return skmetrics.r2_score(meas, sim) 

114 

115 @staticmethod 

116 def calc_mse(meas, sim): 

117 """ 

118 Calculates the MSE (mean square error) 

119 for the given numpy array of measured and simulated data. 

120 

121 :param np.array meas: 

122 Array with measurement data 

123 :param np.array sim: 

124 Array with simulation data 

125 :return: float MSE: 

126 MSE of the given data. 

127 """ 

128 return skmetrics.mean_squared_error(meas, sim) 

129 

130 @staticmethod 

131 def calc_rmse(meas, sim): 

132 """ 

133 Calculates the RMSE (root mean square error) 

134 for the given numpy array of measured and simulated data. 

135 

136 :param np.array meas: 

137 Array with measurement data 

138 :param np.array sim: 

139 Array with simulation data 

140 :return: float RMSE: 

141 RMSE of the given data. 

142 """ 

143 return np.sqrt(skmetrics.mean_squared_error(meas, sim)) 

144 

145 @staticmethod 

146 def calc_nrmse(meas, sim): 

147 """ 

148 Calculates the NRMSE (normalized root mean square error) 

149 for the given numpy array of measured and simulated data. 

150 

151 :param np.array meas: 

152 Array with measurement data 

153 :param np.array sim: 

154 Array with simulation data 

155 :return: float NRMSE: 

156 NRMSE of the given data. 

157 """ 

158 

159 # Check if NRMSE can be calculated 

160 if (np.max(meas) - np.min(meas)) == 0: 

161 raise ValueError("The given measurement data's maximum is equal to " 

162 "it's minimum. This makes the calculation of the " 

163 "NRMSE impossible. Choose another method.") 

164 

165 return np.sqrt(skmetrics.mean_squared_error(meas, sim)) / (np.max(meas) - np.min(meas)) 

166 

167 @staticmethod 

168 def calc_cvrmse(meas, sim): 

169 """ 

170 Calculates the CVRMSE (variance of root mean square error) 

171 THIS IS A TEST 

172 for the given numpy array of measured and simulated data. 

173 

174 :param np.array meas: 

175 Array with measurement data 

176 :param np.array sim: 

177 Array with simulation data 

178 :return: float CVRMSE: 

179 CVRMSE of the given data. 

180 """ 

181 

182 # Check if CVRMSE can be calculated 

183 if np.mean(meas) == 0: 

184 raise ValueError("The given measurement data has a mean of 0. " 

185 "This makes the calculation of the CVRMSE impossible. " 

186 "Choose another method.") 

187 

188 return np.sqrt(skmetrics.mean_squared_error(meas, sim)) / np.mean(meas) 

189 

190 @staticmethod 

191 def calc_nmbe(meas, sim): 

192 """ 

193 Calculates the NMBE (normalized mean bias error) 

194 for the given numpy array of measured and simulated data. 

195  

196 :param np.array meas: 

197 Array with measurement data 

198 :param np.array sim: 

199 Array with simulation data  

200 :return: float NMBE: 

201 NMBE of the given data. 

202 """ 

203 

204 nom = np.sum(meas - sim) 

205 denom = np.sum(meas) 

206 

207 if denom == 0: 

208 raise ValueError("The given measurement data has a sum of 0. " 

209 "This makes the calculation of the NMBE impossible. " 

210 "Choose another method.") 

211 

212 nmbe = nom/denom 

213 return np.abs(nmbe)