Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
18 / 18
CRAP
100.00% covered (success)
100.00%
1 / 1
Error
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
18 / 18
26
100.00% covered (success)
100.00%
1 / 1
 __construct
n/a
0 / 0
n/a
0 / 0
1
 getForecastError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getForecastErrorArray
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getPercentageError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPercentageErrorArray
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getMeanAbsoulteError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMeanAbsoulteDeviation
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getMeanSquaredError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRootMeanSquaredError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCoefficientOfDetermination
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSumSquaredError
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getAdjustedCoefficientOfDetermination
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMeanAbsolutePercentageError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSymmetricMeanAbsolutePercentageError
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getMeanAbsoluteScaledError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMeanSquaredScaledError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getScaledErrorArray
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getScaledError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNaiveForecast
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
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 */
13declare(strict_types=1);
14
15namespace phpOMS\Math\Statistic\Forecast;
16
17use phpOMS\Math\Statistic\Average;
18use phpOMS\Math\Statistic\Correlation;
19use phpOMS\Math\Statistic\MeasureOfDispersion;
20use 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 */
30final 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}