Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
85 / 85
100.00% covered (success)
100.00%
15 / 15
CRAP
100.00% covered (success)
100.00%
1 / 1
MeasureOfDispersion
100.00% covered (success)
100.00%
85 / 85
100.00% covered (success)
100.00%
15 / 15
48
100.00% covered (success)
100.00%
1 / 1
 __construct
n/a
0 / 0
n/a
0 / 0
1
 range
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 empiricalVariationCoefficient
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 standardDeviationSample
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 standardDeviationPopulation
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 sampleVariance
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 empiricalVariance
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
7
 empiricalCovariance
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
 sampleCovariance
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getIQR
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 meanDeviation
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 meanDeviationArray
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 meanAbsoluteDeviation
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 meanAbsoluteDeviationArray
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 squaredMeanDeviation
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 squaredMeanDeviationArray
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Math\Statistic
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;
16
17use phpOMS\Math\Exception\ZeroDivisionException;
18use phpOMS\Math\Matrix\Exception\InvalidDimensionException;
19
20/**
21 * Measure of dispersion.
22 *
23 * @package phpOMS\Math\Statistic
24 * @license OMS License 2.0
25 * @link    https://jingga.app
26 * @since   1.0.0
27 */
28final class MeasureOfDispersion
29{
30    /**
31     * Constructor.
32     *
33     * @since 1.0.0
34     * @codeCoverageIgnore
35     */
36    private function __construct()
37    {
38    }
39
40    /**
41     * Get range.
42     *
43     * Example: ([4, 5, 9, 1, 3])
44     *
45     * @param array<int, int|float> $values Values
46     *
47     * @return float
48     *
49     * @since 1.0.0
50     */
51    public static function range(array $values) : float
52    {
53        \sort($values);
54        $end   = \end($values);
55        $start = \reset($values);
56
57        return $end - $start;
58    }
59
60    /**
61     * Calculage empirical variation coefficient.
62     *
63     * Example: ([4, 5, 9, 1, 3])
64     *
65     * @param array<int, int|float> $values Values
66     * @param float                 $mean   Mean
67     *
68     * @return float
69     *
70     * @throws ZeroDivisionException This exception is thrown if the mean is 0
71     *
72     * @since 1.0.0
73     */
74    public static function empiricalVariationCoefficient(array $values, float $mean = null) : float
75    {
76        $mean = $mean !== null ? $mean : Average::arithmeticMean($values);
77
78        if ($mean === 0.0) {
79            throw new ZeroDivisionException();
80        }
81
82        return self::standardDeviationSample($values) / $mean;
83    }
84
85    /**
86     * Calculate standard deviation of sample.
87     *
88     * Example: ([4, 5, 9, 1, 3])
89     *
90     * @latex \sigma = \sqrt{\sigma^{2}} = \sqrt{Var(X)}
91     *
92     * @param array<int, int|float> $values Values
93     * @param float                 $mean   Mean
94     *
95     * @return float
96     *
97     * @since 1.0.0
98     */
99    public static function standardDeviationSample(array $values, float $mean = null) : float
100    {
101        $mean = $mean !== null ? $mean : Average::arithmeticMean($values);
102        $sum  = 0.0;
103
104        $valueCount = 0;
105
106        foreach ($values as $value) {
107            $sum += ($value - $mean) ** 2;
108            ++$valueCount;
109        }
110
111        return \sqrt($sum / ($valueCount - 1));
112    }
113
114    /**
115     * Calculate standard deviation of entire population
116     *
117     * Example: ([4, 5, 9, 1, 3])
118     *
119     * @latex \sigma = \sqrt{\sigma^{2}} = \sqrt{Var(X)}
120     *
121     * @param array<int, int|float> $values Values
122     * @param float                 $mean   Mean
123     *
124     * @return float
125     *
126     * @since 1.0.0
127     */
128    public static function standardDeviationPopulation(array $values, float $mean = null) : float
129    {
130        $mean = $mean !== null ? $mean : Average::arithmeticMean($values);
131        $sum  = 0.0;
132
133        $valueCount = 0;
134
135        foreach ($values as $value) {
136            $sum += ($value - $mean) ** 2;
137            ++$valueCount;
138        }
139
140        return \sqrt($sum / $valueCount);
141    }
142
143    /**
144     * Calculage sample variance.
145     *
146     * Similar to `empiricalVariance`.
147     *
148     * Example: ([4, 5, 9, 1, 3])
149     *
150     * @latex \sigma^{2} = Var(X) = \frac{1}{N - 1} \sum_{i = 1}^{N}\left(x_{i} - \bar{X}\right)^{2}
151     *
152     * @param array<int, int|float> $values Values
153     * @param float                 $mean   Mean
154     *
155     * @return float
156     *
157     * @throws ZeroDivisionException This exception is thrown if the size of the values array is less than 2
158     *
159     * @since 1.0.0
160     */
161    public static function sampleVariance(array $values, float $mean = null) : float
162    {
163        $count = \count($values);
164
165        if ($count < 2) {
166            throw new ZeroDivisionException();
167        }
168
169        return self::empiricalVariance($values, [], $mean) * $count / ($count - 1);
170    }
171
172    /**
173     * Calculage empirical variance.
174     *
175     * Similar to `sampleVariance`.
176     *
177     * Example: ([4, 5, 9, 1, 3])
178     *
179     * @latex \sigma^{2} = Var(X) = \frac{1}{N} \sum_{i = 1}^{N}\left(x_{i} - \bar{X}\right)^{2}
180     *
181     * @param array<int, int|float> $values        Values
182     * @param array<int, int|float> $probabilities Probabilities
183     * @param float                 $mean          Mean
184     *
185     * @return float
186     *
187     * @throws ZeroDivisionException This exception is thrown if the values array is empty
188     *
189     * @since 1.0.0
190     */
191    public static function empiricalVariance(array $values, array $probabilities = [], float $mean = null) : float
192    {
193        $count          = \count($values);
194        $hasProbability = !empty($probabilities);
195
196        if ($count === 0) {
197            throw new ZeroDivisionException();
198        }
199
200        $mean = $hasProbability ? Average::weightedAverage($values, $probabilities) : ($mean !== null ? $mean : Average::arithmeticMean($values));
201        $sum  = 0;
202
203        foreach ($values as $key => $value) {
204            $sum += ($hasProbability ? $probabilities[$key] : 1) * ($value - $mean) ** 2;
205        }
206
207        return $hasProbability ? $sum : $sum / $count;
208    }
209
210    /**
211     * Calculage empirical covariance.
212     *
213     * Example: ([4, 5, 9, 1, 3], [4, 5, 9, 1, 3])
214     *
215     * @latex cov(X,Y) = \frac{1}{N} \sum_{i = 1}^{N}\left(x_{i} - \bar{X}\right)\left(y_{i} - \bar{Y}\right)
216     *
217     * @param array<int, int|float> $x     Values
218     * @param array<int, int|float> $y     Values
219     * @param float                 $meanX Mean
220     * @param float                 $meanY Mean
221     *
222     * @return float
223     *
224     * @throws ZeroDivisionException     This exception is thrown if the size of the x array is less than 2
225     * @throws InvalidDimensionException This exception is thrown if x and y have different dimensions
226     *
227     * @since 1.0.0
228     */
229    public static function empiricalCovariance(array $x, array $y, float $meanX = null, float $meanY = null) : float
230    {
231        $count = \count($x);
232
233        if ($count < 2) {
234            throw new ZeroDivisionException();
235        }
236
237        if ($count !== \count($y)) {
238            throw new InvalidDimensionException($count . 'x' . \count($y));
239        }
240
241        $xMean = $meanX !== null ? $meanX : Average::arithmeticMean($x);
242        $yMean = $meanY !== null ? $meanY : Average::arithmeticMean($y);
243
244        $sum = 0.0;
245
246        for ($i = 0; $i < $count; ++$i) {
247            $sum += ($x[$i] - $xMean) * ($y[$i] - $yMean);
248        }
249
250        return $sum / $count;
251    }
252
253    /**
254     * Calculage empirical covariance on a sample
255     *
256     * Example: ([4, 5, 9, 1, 3], [4, 5, 9, 1, 3])
257     *
258     * @latex cov(X,Y) = \frac{1}{N - 1} \sum_{i = 1}^{N}\left(x_{i} - \bar{X}\right)\left(y_{i} - \bar{Y}\right)
259     *
260     * @param array<int, int|float> $x     Values
261     * @param array<int, int|float> $y     Values
262     * @param float                 $meanX Mean
263     * @param float                 $meanY Mean
264     *
265     * @return float
266     *
267     * @throws ZeroDivisionException This exception is thrown if the size of the x array is less than 2
268     *
269     * @since 1.0.0
270     */
271    public static function sampleCovariance(array $x, array $y, float $meanX = null, float $meanY = null) : float
272    {
273        $count = \count($x);
274
275        if ($count < 2) {
276            throw new ZeroDivisionException();
277        }
278
279        return self::empiricalCovariance($x, $y, $meanX, $meanY) * $count / ($count - 1);
280    }
281
282    /**
283     * Get interquartile range.
284     *
285     * @param array<int, int|float> $x Dataset
286     *
287     * @return float
288     *
289     * @since 1.0.0
290     */
291    public static function getIQR(array $x) : float
292    {
293        $count = \count($x);
294
295        if ($count % 2 !== 0) {
296            --$count;
297        }
298
299        /** @var int $count */
300        $count /= 2;
301
302        \sort($x);
303
304        $Q1 = Average::median(\array_slice($x, 0, $count));
305        $Q3 = Average::median(\array_slice($x, -$count, $count));
306
307        return $Q3 - $Q1;
308    }
309
310    /**
311     * Get mean deviation.
312     *
313     * @param array<int, int|float> $x      Values
314     * @param float                 $mean   Mean
315     * @param int                   $offset Population/Size offset
316     *
317     * @return float
318     *
319     * @since 1.0.0
320     */
321    public static function meanDeviation(array $x, float $mean = null, int $offset = 0) : float
322    {
323        $mean = $mean !== null ? $mean : Average::arithmeticMean($x);
324        $sum  = 0.0;
325
326        foreach ($x as $xi) {
327            $sum += ($xi - $mean);
328        }
329
330        return $sum / (\count($x) - $offset);
331    }
332
333    /**
334     * Get the deviation to the mean
335     *
336     * @param array<int, int|float> $x Values
337     *
338     * @return array
339     *
340     * @since 1.0.0
341     */
342    public static function meanDeviationArray(array $x, float $mean = null) : array
343    {
344        $mean = $mean !== null ? $mean : Average::arithmeticMean($x);
345
346        foreach ($x as $key => $value) {
347            $x[$key] = $value - $mean;
348        }
349
350        return $x;
351    }
352
353    /**
354     * Get mean absolute deviation (MAD).
355     *
356     * @param array<int, int|float> $x      Values
357     * @param float                 $mean   Mean
358     * @param int                   $offset Population/Size offset
359     *
360     * @return float
361     *
362     * @since 1.0.0
363     */
364    public static function meanAbsoluteDeviation(array $x, float $mean = null, int $offset = 0) : float
365    {
366        $mean = $mean !== null ? $mean : Average::arithmeticMean($x);
367        $sum  = 0.0;
368
369        foreach ($x as $xi) {
370            $sum += \abs($xi - $mean);
371        }
372
373        return $sum / (\count($x) - $offset);
374    }
375
376    /**
377     * Get the deviation to the mean
378     *
379     * @param array<int, int|float> $x Values
380     *
381     * @return array
382     *
383     * @since 1.0.0
384     */
385    public static function meanAbsoluteDeviationArray(array $x, float $mean = null) : array
386    {
387        $mean = $mean !== null ? $mean : Average::arithmeticMean($x);
388
389        foreach ($x as $key => $value) {
390            $x[$key] = \abs($value - $mean);
391        }
392
393        return $x;
394    }
395
396    /**
397     * Get squared mean deviation.
398     *
399     * @param array<int, int|float> $x      Values
400     * @param float                 $mean   Mean
401     * @param int                   $offset Population/Size offset
402     *
403     * @return float
404     *
405     * @since 1.0.0
406     */
407    public static function squaredMeanDeviation(array $x, float $mean = null, int $offset = 0) : float
408    {
409        $mean = $mean !== null ? $mean : Average::arithmeticMean($x);
410        $sum  = 0.0;
411
412        foreach ($x as $xi) {
413            $sum += ($xi - $mean) ** 2;
414        }
415
416        return $sum / (\count($x) - $offset);
417    }
418
419    /**
420     * Get the deviation to the mean squared
421     *
422     * @param array<int, int|float> $x Values
423     *
424     * @return array
425     *
426     * @since 1.0.0
427     */
428    public static function squaredMeanDeviationArray(array $x, float $mean = null) : array
429    {
430        $mean = $mean !== null ? $mean : Average::arithmeticMean($x);
431
432        foreach ($x as $key => $value) {
433            $x[$key] = ($value - $mean) ** 2;
434        }
435
436        return $x;
437    }
438}