• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JBZoo / Utils / 7738455341

28 Jan 2024 08:37AM UTC coverage: 92.758% (-0.4%) from 93.158%
7738455341

push

github

web-flow
Update PHP 8.3 (#47)

14 of 14 new or added lines in 4 files covered. (100.0%)

8 existing lines in 3 files now uncovered.

1665 of 1795 relevant lines covered (92.76%)

41.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.65
/src/Stats.php
1
<?php
2

3
/**
4
 * JBZoo Toolbox - Utils.
5
 *
6
 * This file is part of the JBZoo Toolbox project.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT
11
 * @copyright  Copyright (C) JBZoo.com, All rights reserved.
12
 * @see        https://github.com/JBZoo/Utils
13
 */
14

15
declare(strict_types=1);
16

17
namespace JBZoo\Utils;
18

19
/**
20
 * @see https://github.com/phpbench/phpbench/blob/master/lib/Math/Statistics.php
21
 */
22
final class Stats
23
{
24
    /**
25
     * Returns the standard deviation of a given population.
26
     */
27
    public static function stdDev(array $values, bool $sample = false): float
28
    {
29
        $variance = self::variance($values, $sample);
30✔
30

31
        return \sqrt($variance);
30✔
32
    }
33

34
    /**
35
     * Returns the variance for a given population.
36
     */
37
    public static function variance(array $values, bool $sample = false): float
38
    {
39
        $average = self::mean($values);
30✔
40
        $sum     = 0;
30✔
41

42
        foreach ($values as $value) {
30✔
43
            $diff = ($value - $average) ** 2;
18✔
44
            $sum += $diff;
18✔
45
        }
46

47
        if (\count($values) === 0) {
30✔
48
            return 0;
18✔
49
        }
50

51
        return $sum / (\count($values) - ($sample ? 1 : 0));
18✔
52
    }
53

54
    /**
55
     * Returns the mean (average) value of the given values.
56
     */
57
    public static function mean(?array $values): float
58
    {
59
        if ($values === null || \count($values) === 0) {
36✔
60
            return 0;
24✔
61
        }
62

63
        \array_walk($values, static function (null|float|int|string &$value): void {
24✔
64
            $value = float($value);
24✔
65
        });
24✔
66
        $sum = \array_sum($values);
24✔
67

68
        if ($sum === 0) {
24✔
UNCOV
69
            return 0;
×
70
        }
71

72
        $count = \count($values);
24✔
73

74
        return \round($sum / $count, 9);
24✔
75
    }
76

77
    /**
78
     * Returns an array populated with $num numbers from $min to $max.
79
     * @return float[]
80
     */
81
    public static function linSpace(float $min, float $max, int $num = 50, bool $endpoint = true): array
82
    {
83
        $range = $max - $min;
6✔
84

85
        if ($max === $min) {
6✔
86
            throw new Exception("Min and max cannot be the same number: {$max}");
×
87
        }
88

89
        $unit  = $range / ($endpoint ? $num - 1 : $num);
6✔
90
        $space = [];
6✔
91

92
        for ($value = $min; $value <= $max; $value += $unit) {
6✔
93
            $space[] = $value;
6✔
94
        }
95

96
        if (!$endpoint) {
6✔
97
            \array_pop($space);
6✔
98
        }
99

100
        return $space;
6✔
101
    }
102

103
    /**
104
     * Generate a histogram. Note this is not a great function, and should not be relied upon for serious use.
105
     * @see http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.histogram.html
106
     * @param float[] $values
107
     */
108
    public static function histogram(
109
        array $values,
110
        int $steps = 10,
111
        ?float $lowerBound = null,
112
        ?float $upperBound = null,
113
    ): array {
114
        if (\count($values) === 0) {
6✔
115
            throw new Exception('Empty array of values is given');
×
116
        }
117

118
        $min = $lowerBound ?? \min($values);
6✔
119
        $max = $upperBound ?? \max($values);
6✔
120

121
        $range = $max - $min;
6✔
122

123
        $step = $range / $steps;
6✔
124
        $steps++; // add one extra step to catch the max value
6✔
125

126
        $histogram = [];
6✔
127

128
        $floor = $min;
6✔
129

130
        for ($i = 0; $i < $steps; $i++) {
6✔
131
            $ceil = $floor + $step;
6✔
132

133
            if (!isset($histogram[(string)$floor])) {
6✔
134
                $histogram[(string)$floor] = 0;
6✔
135
            }
136

137
            foreach ($values as $value) {
6✔
138
                if ($value >= $floor && $value < $ceil) {
6✔
139
                    $histogram[(string)$floor]++;
6✔
140
                }
141
            }
142

143
            $floor += $step;
6✔
144
        }
145

146
        return $histogram;
6✔
147
    }
148

149
    /**
150
     * Render human readable string of average value and system error.
151
     */
152
    public static function renderAverage(array $values, int $rounding = 3): string
153
    {
154
        $avg    = \number_format(\round(self::mean($values), $rounding), $rounding);
12✔
155
        $stdDev = \number_format(\round(self::stdDev($values), $rounding), $rounding);
12✔
156

157
        return "{$avg}±{$stdDev}";
12✔
158
    }
159

160
    /**
161
     * Render human readable string of average value and system error.
162
     */
163
    public static function renderMedian(array $values, int $rounding = 3): string
164
    {
165
        $median = \number_format(\round(self::median($values), $rounding), $rounding);
12✔
166
        $stdDev = \number_format(\round(self::stdDev($values), $rounding), $rounding);
12✔
167

168
        return "{$median}±{$stdDev}";
12✔
169
    }
170

171
    /**
172
     * Calculate the percentile of a given population.
173
     * @param float[]|int[] $data
174
     */
175
    public static function percentile(array $data, float|int $percentile = 95): float
176
    {
177
        $count = \count($data);
36✔
178
        if ($count === 0) {
36✔
179
            return 0;
18✔
180
        }
181

182
        $percent = $percentile / 100;
30✔
183
        if ($percent < 0 || $percent > 1) {
30✔
184
            throw new Exception("Percentile should be between 0 and 100, {$percentile} given");
12✔
185
        }
186

187
        $allIndex   = ($count - 1) * $percent;
18✔
188
        $intValue   = (int)$allIndex;
18✔
189
        $floatValue = $allIndex - $intValue;
18✔
190

191
        \sort($data, \SORT_NUMERIC);
18✔
192

193
        if ($intValue + 1 < $count) {
18✔
194
            $result = $data[$intValue] + ($data[$intValue + 1] - $data[$intValue]) * $floatValue;
18✔
195
        } else {
196
            $result = $data[$intValue];
12✔
197
        }
198

199
        return \round(float($result), 6);
18✔
200
    }
201

202
    /**
203
     * Calculate the median of a given population.
204
     * @param float[]|int[] $data
205
     */
206
    public static function median(array $data): float
207
    {
208
        return self::percentile($data, 50.0);
18✔
209
    }
210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc