Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
4
 
5
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
6
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
7
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
8
 
9
class Percentiles
10
{
11
    public const RANK_SORT_DESCENDING = 0;
12
 
13
    public const RANK_SORT_ASCENDING = 1;
14
 
15
    /**
16
     * PERCENTILE.
17
     *
18
     * Returns the nth percentile of values in a range..
19
     *
20
     * Excel Function:
21
     *        PERCENTILE(value1[,value2[, ...]],entry)
22
     *
23
     * @param mixed $args Data values
24
     *
25
     * @return float|string The result, or a string containing an error
26
     */
27
    public static function PERCENTILE(mixed ...$args)
28
    {
29
        $aArgs = Functions::flattenArray($args);
30
 
31
        // Calculate
32
        $entry = array_pop($aArgs);
33
 
34
        try {
35
            $entry = StatisticalValidations::validateFloat($entry);
36
        } catch (Exception $e) {
37
            return $e->getMessage();
38
        }
39
 
40
        if (($entry < 0) || ($entry > 1)) {
41
            return ExcelError::NAN();
42
        }
43
 
44
        $mArgs = self::percentileFilterValues($aArgs);
45
        $mValueCount = count($mArgs);
46
        if ($mValueCount > 0) {
47
            sort($mArgs);
48
            $count = Counts::COUNT($mArgs);
49
            $index = $entry * ($count - 1);
50
            $indexFloor = floor($index);
51
            $iBase = (int) $indexFloor;
52
            if ($index == $indexFloor) {
53
                return $mArgs[$iBase];
54
            }
55
            $iNext = $iBase + 1;
56
            $iProportion = $index - $iBase;
57
 
58
            return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
59
        }
60
 
61
        return ExcelError::NAN();
62
    }
63
 
64
    /**
65
     * PERCENTRANK.
66
     *
67
     * Returns the rank of a value in a data set as a percentage of the data set.
68
     * Note that the returned rank is simply rounded to the appropriate significant digits,
69
     *      rather than floored (as MS Excel), so value 3 for a value set of  1, 2, 3, 4 will return
70
     *      0.667 rather than 0.666
71
     *
72
     * @param mixed $valueSet An array of (float) values, or a reference to, a list of numbers
73
     * @param mixed $value The number whose rank you want to find
74
     * @param mixed $significance The (integer) number of significant digits for the returned percentage value
75
     *
76
     * @return float|string (string if result is an error)
77
     */
78
    public static function PERCENTRANK(mixed $valueSet, mixed $value, mixed $significance = 3): string|float
79
    {
80
        $valueSet = Functions::flattenArray($valueSet);
81
        $value = Functions::flattenSingleValue($value);
82
        $significance = ($significance === null) ? 3 : Functions::flattenSingleValue($significance);
83
 
84
        try {
85
            $value = StatisticalValidations::validateFloat($value);
86
            $significance = StatisticalValidations::validateInt($significance);
87
        } catch (Exception $e) {
88
            return $e->getMessage();
89
        }
90
 
91
        $valueSet = self::rankFilterValues($valueSet);
92
        $valueCount = count($valueSet);
93
        if ($valueCount == 0) {
94
            return ExcelError::NA();
95
        }
96
        sort($valueSet, SORT_NUMERIC);
97
 
98
        $valueAdjustor = $valueCount - 1;
99
        if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
100
            return ExcelError::NA();
101
        }
102
 
103
        $pos = array_search($value, $valueSet);
104
        if ($pos === false) {
105
            $pos = 0;
106
            $testValue = $valueSet[0];
107
            while ($testValue < $value) {
108
                $testValue = $valueSet[++$pos];
109
            }
110
            --$pos;
111
            $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
112
        }
113
 
114
        return round(((float) $pos) / $valueAdjustor, $significance);
115
    }
116
 
117
    /**
118
     * QUARTILE.
119
     *
120
     * Returns the quartile of a data set.
121
     *
122
     * Excel Function:
123
     *        QUARTILE(value1[,value2[, ...]],entry)
124
     *
125
     * @param mixed $args Data values
126
     *
127
     * @return float|string The result, or a string containing an error
128
     */
129
    public static function QUARTILE(mixed ...$args)
130
    {
131
        $aArgs = Functions::flattenArray($args);
132
        $entry = array_pop($aArgs);
133
 
134
        try {
135
            $entry = StatisticalValidations::validateFloat($entry);
136
        } catch (Exception $e) {
137
            return $e->getMessage();
138
        }
139
 
140
        $entry = floor($entry);
141
        $entry /= 4;
142
        if (($entry < 0) || ($entry > 1)) {
143
            return ExcelError::NAN();
144
        }
145
 
146
        return self::PERCENTILE($aArgs, $entry);
147
    }
148
 
149
    /**
150
     * RANK.
151
     *
152
     * Returns the rank of a number in a list of numbers.
153
     *
154
     * @param mixed $value The number whose rank you want to find
155
     * @param mixed $valueSet An array of float values, or a reference to, a list of numbers
156
     * @param mixed $order Order to sort the values in the value set
157
     *
158
     * @return float|string The result, or a string containing an error (0 = Descending, 1 = Ascending)
159
     */
160
    public static function RANK(mixed $value, mixed $valueSet, mixed $order = self::RANK_SORT_DESCENDING)
161
    {
162
        $value = Functions::flattenSingleValue($value);
163
        $valueSet = Functions::flattenArray($valueSet);
164
        $order = ($order === null) ? self::RANK_SORT_DESCENDING : Functions::flattenSingleValue($order);
165
 
166
        try {
167
            $value = StatisticalValidations::validateFloat($value);
168
            $order = StatisticalValidations::validateInt($order);
169
        } catch (Exception $e) {
170
            return $e->getMessage();
171
        }
172
 
173
        $valueSet = self::rankFilterValues($valueSet);
174
        if ($order === self::RANK_SORT_DESCENDING) {
175
            rsort($valueSet, SORT_NUMERIC);
176
        } else {
177
            sort($valueSet, SORT_NUMERIC);
178
        }
179
 
180
        $pos = array_search($value, $valueSet);
181
        if ($pos === false) {
182
            return ExcelError::NA();
183
        }
184
 
185
        return ++$pos;
186
    }
187
 
188
    protected static function percentileFilterValues(array $dataSet): array
189
    {
190
        return array_filter(
191
            $dataSet,
192
            fn ($value): bool => is_numeric($value) && !is_string($value)
193
        );
194
    }
195
 
196
    protected static function rankFilterValues(array $dataSet): array
197
    {
198
        return array_filter(
199
            $dataSet,
200
            fn ($value): bool => is_numeric($value)
201
        );
202
    }
203
}