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
« 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."""
4import numpy as np
5import sklearn.metrics as skmetrics
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).
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)
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.
28 Example:
30 >>> def my_func(x, y)
31 >>> return sum(x - y)
32 >>> StatisticsAnalyzer(method=my_func)
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 """
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}
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}
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.")
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]
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)
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.
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)
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.
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)
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.
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)
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.
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))
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.
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 """
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.")
165 return np.sqrt(skmetrics.mean_squared_error(meas, sim)) / (np.max(meas) - np.min(meas))
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.
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 """
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.")
188 return np.sqrt(skmetrics.mean_squared_error(meas, sim)) / np.mean(meas)
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.
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 """
204 nom = np.sum(meas - sim)
205 denom = np.sum(meas)
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.")
212 nmbe = nom/denom
213 return np.abs(nmbe)