Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
85 / 85 |
|
100.00% |
15 / 15 |
CRAP | |
100.00% |
1 / 1 |
MeasureOfDispersion | |
100.00% |
85 / 85 |
|
100.00% |
15 / 15 |
48 | |
100.00% |
1 / 1 |
__construct | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
range | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
empiricalVariationCoefficient | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
standardDeviationSample | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
standardDeviationPopulation | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
sampleVariance | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
empiricalVariance | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
7 | |||
empiricalCovariance | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
6 | |||
sampleCovariance | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getIQR | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
meanDeviation | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
meanDeviationArray | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
meanAbsoluteDeviation | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
meanAbsoluteDeviationArray | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
squaredMeanDeviation | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
squaredMeanDeviationArray | |
100.00% |
4 / 4 |
|
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 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Math\Statistic; |
16 | |
17 | use phpOMS\Math\Exception\ZeroDivisionException; |
18 | use 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 | */ |
28 | final 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 | } |