Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
4.35% covered (danger)
4.35%
1 / 23
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
Forecasts
4.35% covered (danger)
4.35%
1 / 23
50.00% covered (danger)
50.00%
1 / 2
49.88
0.00% covered (danger)
0.00%
0 / 1
 getForecastInteval
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 simpleSeasonalForecast
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
42
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
17/**
18 * General forecasts helper class.
19 *
20 * @package phpOMS\Math\Statistic\Forecast
21 * @license OMS License 2.0
22 * @link    https://jingga.app
23 * @since   1.0.0
24 */
25final class Forecasts
26{
27    /**
28     * Get forecast/prediction interval.
29     *
30     * @param float $forecast          Forecast value
31     * @param float $standardDeviation Standard Deviation of forecast
32     * @param float $interval          Forecast multiplier for prediction intervals
33     *
34     * @return array<int|float>
35     *
36     * @since 1.0.0
37     */
38    public static function getForecastInteval(float $forecast, float $standardDeviation, float $interval = 1.96) : array
39    {
40        return [$forecast - $interval * $standardDeviation, $forecast + $interval * $standardDeviation];
41    }
42
43    /**
44     * Simple seasonal forecast.
45     *
46     * @param array<int|float> $history     History
47     * @param int              $periods     Number of periods to forecast
48     * @param int              $seasonality Seasonality
49     *
50     * @return array<int|float>
51     *
52     * @since 1.0.0
53     */
54    public static function simpleSeasonalForecast(array $history, int $periods, int $seasonality = 1) : array
55    {
56        $size = \count($history);
57        $avg  = \array_sum($history) / $size;
58
59        $variance = 0;
60        foreach ($history as $sale) {
61            $variance += \pow($sale - $avg, 2);
62        }
63
64        $variance    /= $size;
65        $stdDeviation = \sqrt($variance);
66
67        // Calculate the seasonal index for each period
68        $seasonalIndex = [];
69        for ($i = 0; $i < $seasonality; ++$i) {
70            $seasonalIndex[$i] = 0;
71            $count             = 0;
72
73            for ($j = $i; $j < $size; $j += $seasonality) {
74                $seasonalIndex[$i] += $history[$j];
75                ++$count;
76            }
77
78            if ($count > 0) {
79                $seasonalIndex[$i] /= $count;
80                $seasonalIndex[$i] /= $avg;
81            }
82        }
83
84        // Forecast the next periods
85        $forecast = [];
86        for ($i = 1; $i <= $periods; ++$i) {
87            $seasonalMultiplier = $seasonalIndex[($i - 1) % $seasonality];
88            $forecast[]         = $avg * $seasonalMultiplier + ($stdDeviation * $i);
89        }
90
91        return $forecast;
92    }
93}