Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
41 / 41 |
|
100.00% |
18 / 18 |
CRAP | |
100.00% |
1 / 1 |
Error | |
100.00% |
41 / 41 |
|
100.00% |
18 / 18 |
26 | |
100.00% |
1 / 1 |
__construct | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
getForecastError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getForecastErrorArray | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getPercentageError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPercentageErrorArray | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getMeanAbsoulteError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMeanAbsoulteDeviation | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getMeanSquaredError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRootMeanSquaredError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getCoefficientOfDetermination | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSumSquaredError | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getAdjustedCoefficientOfDetermination | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMeanAbsolutePercentageError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSymmetricMeanAbsolutePercentageError | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getMeanAbsoluteScaledError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMeanSquaredScaledError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getScaledErrorArray | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getScaledError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getNaiveForecast | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Math\Statistic\Forecast |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Math\Statistic\Forecast; |
16 | |
17 | use phpOMS\Math\Statistic\Average; |
18 | use phpOMS\Math\Statistic\Correlation; |
19 | use phpOMS\Math\Statistic\MeasureOfDispersion; |
20 | use phpOMS\Utils\ArrayUtils; |
21 | |
22 | /** |
23 | * Basic forecast functions. |
24 | * |
25 | * @package phpOMS\Math\Statistic\Forecast |
26 | * @license OMS License 2.0 |
27 | * @link https://jingga.app |
28 | * @since 1.0.0 |
29 | */ |
30 | final class Error |
31 | { |
32 | /** |
33 | * Constructor. |
34 | * |
35 | * @since 1.0.0 |
36 | * @codeCoverageIgnore |
37 | */ |
38 | private function __construct() |
39 | { |
40 | } |
41 | |
42 | /** |
43 | * Get the error of a forecast. |
44 | * |
45 | * @param float $observed Dataset |
46 | * @param float $forecasted Forecasted |
47 | * |
48 | * @return float |
49 | * |
50 | * @since 1.0.0 |
51 | */ |
52 | public static function getForecastError(float $observed, float $forecasted) : float |
53 | { |
54 | return $observed - $forecasted; |
55 | } |
56 | |
57 | /** |
58 | * Get array of errors of a forecast. |
59 | * |
60 | * @param float[] $observed Dataset |
61 | * @param float[] $forecasted Forecasted |
62 | * |
63 | * @return float[] |
64 | * |
65 | * @since 1.0.0 |
66 | */ |
67 | public static function getForecastErrorArray(array $observed, array $forecasted) : array |
68 | { |
69 | $errors = []; |
70 | |
71 | foreach ($forecasted as $key => $expected) { |
72 | $errors[] = self::getForecastError($observed[$key], $expected); |
73 | } |
74 | |
75 | return $errors; |
76 | } |
77 | |
78 | /** |
79 | * Get error percentage. |
80 | * |
81 | * @param float $error Error |
82 | * @param float $observed Dataset |
83 | * |
84 | * @return float |
85 | * |
86 | * @since 1.0.0 |
87 | */ |
88 | public static function getPercentageError(float $error, float $observed) : float |
89 | { |
90 | return $error / $observed; |
91 | } |
92 | |
93 | /** |
94 | * Get error percentages. |
95 | * |
96 | * @param float[] $errors Errors |
97 | * @param float[] $observed Dataset |
98 | * |
99 | * @return float[] |
100 | * |
101 | * @since 1.0.0 |
102 | */ |
103 | public static function getPercentageErrorArray(array $errors, array $observed) : array |
104 | { |
105 | $percentages = []; |
106 | |
107 | foreach ($errors as $key => $error) { |
108 | $percentages[] = self::getPercentageError($error, $observed[$key]); |
109 | } |
110 | |
111 | return $percentages; |
112 | } |
113 | |
114 | /** |
115 | * Get mean absolute error (MAE). |
116 | * |
117 | * @param array<int, int|float> $errors Errors |
118 | * |
119 | * @return float |
120 | * |
121 | * @since 1.0.0 |
122 | */ |
123 | public static function getMeanAbsoulteError(array $errors) : float |
124 | { |
125 | return MeasureOfDispersion::meanAbsoluteDeviation($errors); |
126 | } |
127 | |
128 | /** |
129 | * Get mean absolute deviation (MAD). |
130 | * |
131 | * @param array<int, int|float> $observed Observed values |
132 | * @param array<int, int|float> $forecasted Forecasted values |
133 | * |
134 | * @return float |
135 | * |
136 | * @since 1.0.0 |
137 | */ |
138 | public static function getMeanAbsoulteDeviation(array $observed, array $forecasted) : float |
139 | { |
140 | $deviation = 0.0; |
141 | foreach ($observed as $key => $value) { |
142 | $deviation += \abs($value - $forecasted[$key]); |
143 | } |
144 | |
145 | return $deviation / \count($observed); |
146 | } |
147 | |
148 | /** |
149 | * Get mean squared error (MSE). |
150 | * |
151 | * @param array<int, int|float> $errors Errors |
152 | * @param int $offset Population/Size offset |
153 | * |
154 | * @return float |
155 | * |
156 | * @since 1.0.0 |
157 | */ |
158 | public static function getMeanSquaredError(array $errors, int $offset = 0) : float |
159 | { |
160 | return MeasureOfDispersion::squaredMeanDeviation($errors, null, $offset); |
161 | } |
162 | |
163 | /** |
164 | * Get root mean squared error (RMSE). |
165 | * |
166 | * @param array<int, int|float> $errors Errors |
167 | * |
168 | * @return float |
169 | * |
170 | * @since 1.0.0 |
171 | */ |
172 | public static function getRootMeanSquaredError(array $errors) : float |
173 | { |
174 | return \sqrt(Average::arithmeticMean(ArrayUtils::power($errors, 2))); |
175 | } |
176 | |
177 | /** |
178 | * Goodness of fit (R-squared) |
179 | * |
180 | * Evaluating how well the observed data fit the linear regression model. |
181 | * |
182 | * @latex R^{2} = \frac{\sum \left(\hat{y}_{i} - \bar{y}\right)^2}{\sum \left(y_{i} - \bar{y}\right)^2} |
183 | * |
184 | * @param float[] $observed Obersved y values |
185 | * @param float[] $forecasted Forecasted y values |
186 | * |
187 | * @return float |
188 | * |
189 | * @since 1.0.0 |
190 | */ |
191 | public static function getCoefficientOfDetermination(array $observed, array $forecasted) : float |
192 | { |
193 | return Correlation::bravaisPersonCorrelationCoefficientPopulation($observed, $forecasted) ** 2; |
194 | } |
195 | |
196 | /** |
197 | * Get sum squared error (SSE). |
198 | * |
199 | * @param array<int, int|float> $errors Errors |
200 | * |
201 | * @return float |
202 | * |
203 | * @since 1.0.0 |
204 | */ |
205 | public static function getSumSquaredError(array $errors) : float |
206 | { |
207 | $error = 0.0; |
208 | |
209 | foreach ($errors as $e) { |
210 | $error += $e * $e; |
211 | } |
212 | |
213 | return $error; |
214 | } |
215 | |
216 | /** |
217 | * Get Adjusted coefficient of determination (R Bar Squared) |
218 | * |
219 | * @param float $R R |
220 | * @param int $observations Amount of observations |
221 | * @param int $predictors Amount of predictors |
222 | * |
223 | * @return float |
224 | * |
225 | * @since 1.0.0 |
226 | */ |
227 | public static function getAdjustedCoefficientOfDetermination(float $R, int $observations, int $predictors) : float |
228 | { |
229 | return 1 - (1 - $R) * ($observations - 1) / ($observations - $predictors - 1); |
230 | } |
231 | |
232 | /** |
233 | * Get mean absolute percentage error (MAPE). |
234 | * |
235 | * @param float[] $observed Dataset |
236 | * @param float[] $forecasted Forecasted |
237 | * |
238 | * @return float |
239 | * |
240 | * @since 1.0.0 |
241 | */ |
242 | public static function getMeanAbsolutePercentageError(array $observed, array $forecasted) : float |
243 | { |
244 | return Average::arithmeticMean(ArrayUtils::abs(self::getPercentageErrorArray(self::getForecastErrorArray($observed, $forecasted), $observed))); |
245 | } |
246 | |
247 | /** |
248 | * Get mean absolute percentage error (sMAPE). |
249 | * |
250 | * @param float[] $observed Dataset |
251 | * @param float[] $forecasted Forecasted |
252 | * |
253 | * @return float |
254 | * |
255 | * @since 1.0.0 |
256 | */ |
257 | public static function getSymmetricMeanAbsolutePercentageError(array $observed, array $forecasted) : float |
258 | { |
259 | $error = []; |
260 | |
261 | foreach ($observed as $key => $value) { |
262 | $error[] = \abs($value - $forecasted[$key]) / ($value + $forecasted[$key]) / 2; |
263 | } |
264 | |
265 | return Average::arithmeticMean($error); |
266 | } |
267 | |
268 | /** |
269 | * Get mean absolute scaled error (MASE) |
270 | * |
271 | * @param array<int, int|float> $scaledErrors Scaled errors |
272 | * |
273 | * @return float |
274 | * |
275 | * @since 1.0.0 |
276 | */ |
277 | public static function getMeanAbsoluteScaledError(array $scaledErrors) : float |
278 | { |
279 | return Average::arithmeticMean(ArrayUtils::abs($scaledErrors)); |
280 | } |
281 | |
282 | /** |
283 | * Get mean squared scaled error (MSSE) |
284 | * |
285 | * @param array<int, int|float> $scaledErrors Scaled errors |
286 | * |
287 | * @return float |
288 | * |
289 | * @since 1.0.0 |
290 | */ |
291 | public static function getMeanSquaredScaledError(array $scaledErrors) : float |
292 | { |
293 | return Average::arithmeticMean(ArrayUtils::power($scaledErrors, 2)); |
294 | } |
295 | |
296 | /** |
297 | * Get scaled error (SE) |
298 | * |
299 | * @param array<int, int|float> $errors Errors |
300 | * @param float[] $observed Dataset |
301 | * @param int $m Shift |
302 | * |
303 | * @return array |
304 | * |
305 | * @since 1.0.0 |
306 | */ |
307 | public static function getScaledErrorArray(array $errors, array $observed, int $m = 1) : array |
308 | { |
309 | $scaled = []; |
310 | $naive = 1 / (\count($observed) - $m) * self::getNaiveForecast($observed, $m); |
311 | |
312 | foreach ($errors as $error) { |
313 | $scaled[] = $error / $naive; |
314 | } |
315 | |
316 | return $scaled; |
317 | } |
318 | |
319 | /** |
320 | * Get scaled error (SE) |
321 | * |
322 | * @param float $error Errors |
323 | * @param float[] $observed Dataset |
324 | * @param int $m Shift |
325 | * |
326 | * @return float |
327 | * |
328 | * @since 1.0.0 |
329 | */ |
330 | public static function getScaledError(float $error, array $observed, int $m = 1) : float |
331 | { |
332 | return $error / (1 / (\count($observed) - $m) * self::getNaiveForecast($observed, $m)); |
333 | } |
334 | |
335 | /** |
336 | * Get naive forecast |
337 | * |
338 | * @param float[] $observed Dataset |
339 | * @param int $m Shift |
340 | * |
341 | * @return float |
342 | * |
343 | * @since 1.0.0 |
344 | */ |
345 | private static function getNaiveForecast(array $observed, int $m = 1) : float |
346 | { |
347 | $sum = 0.0; |
348 | $count = \count($observed); |
349 | |
350 | for ($i = 0 + $m; $i < $count; ++$i) { |
351 | $sum += \abs($observed[$i] - $observed[$i - $m]); |
352 | } |
353 | |
354 | return $sum; |
355 | } |
356 | } |