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\ArrayEnabled;
6
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
7
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
8
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
9
use PhpOffice\PhpSpreadsheet\Shared\Trend\Trend;
10
 
11
class Trends
12
{
13
    use ArrayEnabled;
14
 
15
    private static function filterTrendValues(array &$array1, array &$array2): void
16
    {
17
        foreach ($array1 as $key => $value) {
18
            if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
19
                unset($array1[$key], $array2[$key]);
20
            }
21
        }
22
    }
23
 
24
    /**
25
     * @param mixed $array1 should be array, but scalar is made into one
26
     * @param mixed $array2 should be array, but scalar is made into one
27
     */
28
    private static function checkTrendArrays(mixed &$array1, mixed &$array2): void
29
    {
30
        if (!is_array($array1)) {
31
            $array1 = [$array1];
32
        }
33
        if (!is_array($array2)) {
34
            $array2 = [$array2];
35
        }
36
 
37
        $array1 = Functions::flattenArray($array1);
38
        $array2 = Functions::flattenArray($array2);
39
 
40
        self::filterTrendValues($array1, $array2);
41
        self::filterTrendValues($array2, $array1);
42
 
43
        // Reset the array indexes
44
        $array1 = array_merge($array1);
45
        $array2 = array_merge($array2);
46
    }
47
 
48
    protected static function validateTrendArrays(array $yValues, array $xValues): void
49
    {
50
        $yValueCount = count($yValues);
51
        $xValueCount = count($xValues);
52
 
53
        if (($yValueCount === 0) || ($yValueCount !== $xValueCount)) {
54
            throw new Exception(ExcelError::NA());
55
        } elseif ($yValueCount === 1) {
56
            throw new Exception(ExcelError::DIV0());
57
        }
58
    }
59
 
60
    /**
61
     * CORREL.
62
     *
63
     * Returns covariance, the average of the products of deviations for each data point pair.
64
     *
65
     * @param mixed $yValues array of mixed Data Series Y
66
     * @param null|mixed $xValues array of mixed Data Series X
67
     */
68
    public static function CORREL(mixed $yValues, $xValues = null): float|string
69
    {
70
        if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
71
            return ExcelError::VALUE();
72
        }
73
 
74
        try {
75
            self::checkTrendArrays($yValues, $xValues);
76
            self::validateTrendArrays($yValues, $xValues);
77
        } catch (Exception $e) {
78
            return $e->getMessage();
79
        }
80
 
81
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
82
 
83
        return $bestFitLinear->getCorrelation();
84
    }
85
 
86
    /**
87
     * COVAR.
88
     *
89
     * Returns covariance, the average of the products of deviations for each data point pair.
90
     *
91
     * @param mixed[] $yValues array of mixed Data Series Y
92
     * @param mixed[] $xValues array of mixed Data Series X
93
     */
94
    public static function COVAR(array $yValues, array $xValues): float|string
95
    {
96
        try {
97
            self::checkTrendArrays($yValues, $xValues);
98
            self::validateTrendArrays($yValues, $xValues);
99
        } catch (Exception $e) {
100
            return $e->getMessage();
101
        }
102
 
103
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
104
 
105
        return $bestFitLinear->getCovariance();
106
    }
107
 
108
    /**
109
     * FORECAST.
110
     *
111
     * Calculates, or predicts, a future value by using existing values.
112
     * The predicted value is a y-value for a given x-value.
113
     *
114
     * @param mixed $xValue Float value of X for which we want to find Y
115
     *                      Or can be an array of values
116
     * @param mixed[] $yValues array of mixed Data Series Y
117
     * @param mixed[] $xValues array of mixed Data Series X
118
     *
119
     * @return array|bool|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
120
     *            with the same dimensions
121
     */
122
    public static function FORECAST(mixed $xValue, array $yValues, array $xValues)
123
    {
124
        if (is_array($xValue)) {
125
            return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $xValue, $yValues, $xValues);
126
        }
127
 
128
        try {
129
            $xValue = StatisticalValidations::validateFloat($xValue);
130
            self::checkTrendArrays($yValues, $xValues);
131
            self::validateTrendArrays($yValues, $xValues);
132
        } catch (Exception $e) {
133
            return $e->getMessage();
134
        }
135
 
136
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
137
 
138
        return $bestFitLinear->getValueOfYForX($xValue);
139
    }
140
 
141
    /**
142
     * GROWTH.
143
     *
144
     * Returns values along a predicted exponential Trend
145
     *
146
     * @param mixed[] $yValues Data Series Y
147
     * @param mixed[] $xValues Data Series X
148
     * @param mixed[] $newValues Values of X for which we want to find Y
149
     * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
150
     *
151
     * @return array<int, array<int, array<int, float>>>
152
     */
153
    public static function GROWTH(array $yValues, array $xValues = [], array $newValues = [], mixed $const = true): array
154
    {
155
        $yValues = Functions::flattenArray($yValues);
156
        $xValues = Functions::flattenArray($xValues);
157
        $newValues = Functions::flattenArray($newValues);
158
        $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
159
 
160
        $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
161
        if (empty($newValues)) {
162
            $newValues = $bestFitExponential->getXValues();
163
        }
164
 
165
        $returnArray = [];
166
        foreach ($newValues as $xValue) {
167
            $returnArray[0][] = [$bestFitExponential->getValueOfYForX($xValue)];
168
        }
169
 
170
        return $returnArray;
171
    }
172
 
173
    /**
174
     * INTERCEPT.
175
     *
176
     * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
177
     *
178
     * @param mixed[] $yValues Data Series Y
179
     * @param mixed[] $xValues Data Series X
180
     */
181
    public static function INTERCEPT(array $yValues, array $xValues): float|string
182
    {
183
        try {
184
            self::checkTrendArrays($yValues, $xValues);
185
            self::validateTrendArrays($yValues, $xValues);
186
        } catch (Exception $e) {
187
            return $e->getMessage();
188
        }
189
 
190
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
191
 
192
        return $bestFitLinear->getIntersect();
193
    }
194
 
195
    /**
196
     * LINEST.
197
     *
198
     * Calculates the statistics for a line by using the "least squares" method to calculate a straight line
199
     *     that best fits your data, and then returns an array that describes the line.
200
     *
201
     * @param mixed[] $yValues Data Series Y
202
     * @param null|mixed[] $xValues Data Series X
203
     * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
204
     * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
205
     *
206
     * @return array|string The result, or a string containing an error
207
     */
208
    public static function LINEST(array $yValues, ?array $xValues = null, mixed $const = true, mixed $stats = false): string|array
209
    {
210
        $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
211
        $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
212
        if ($xValues === null) {
213
            $xValues = $yValues;
214
        }
215
 
216
        try {
217
            self::checkTrendArrays($yValues, $xValues);
218
            self::validateTrendArrays($yValues, $xValues);
219
        } catch (Exception $e) {
220
            return $e->getMessage();
221
        }
222
 
223
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
224
 
225
        if ($stats === true) {
226
            return [
227
                [
228
                    $bestFitLinear->getSlope(),
229
                    $bestFitLinear->getIntersect(),
230
                ],
231
                [
232
                    $bestFitLinear->getSlopeSE(),
233
                    ($const === false) ? ExcelError::NA() : $bestFitLinear->getIntersectSE(),
234
                ],
235
                [
236
                    $bestFitLinear->getGoodnessOfFit(),
237
                    $bestFitLinear->getStdevOfResiduals(),
238
                ],
239
                [
240
                    $bestFitLinear->getF(),
241
                    $bestFitLinear->getDFResiduals(),
242
                ],
243
                [
244
                    $bestFitLinear->getSSRegression(),
245
                    $bestFitLinear->getSSResiduals(),
246
                ],
247
            ];
248
        }
249
 
250
        return [
251
            $bestFitLinear->getSlope(),
252
            $bestFitLinear->getIntersect(),
253
        ];
254
    }
255
 
256
    /**
257
     * LOGEST.
258
     *
259
     * Calculates an exponential curve that best fits the X and Y data series,
260
     *        and then returns an array that describes the line.
261
     *
262
     * @param mixed[] $yValues Data Series Y
263
     * @param null|mixed[] $xValues Data Series X
264
     * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
265
     * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
266
     *
267
     * @return array|string The result, or a string containing an error
268
     */
269
    public static function LOGEST(array $yValues, ?array $xValues = null, mixed $const = true, mixed $stats = false): string|array
270
    {
271
        $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
272
        $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
273
        if ($xValues === null) {
274
            $xValues = $yValues;
275
        }
276
 
277
        try {
278
            self::checkTrendArrays($yValues, $xValues);
279
            self::validateTrendArrays($yValues, $xValues);
280
        } catch (Exception $e) {
281
            return $e->getMessage();
282
        }
283
 
284
        foreach ($yValues as $value) {
285
            if ($value < 0.0) {
286
                return ExcelError::NAN();
287
            }
288
        }
289
 
290
        $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
291
 
292
        if ($stats === true) {
293
            return [
294
                [
295
                    $bestFitExponential->getSlope(),
296
                    $bestFitExponential->getIntersect(),
297
                ],
298
                [
299
                    $bestFitExponential->getSlopeSE(),
300
                    ($const === false) ? ExcelError::NA() : $bestFitExponential->getIntersectSE(),
301
                ],
302
                [
303
                    $bestFitExponential->getGoodnessOfFit(),
304
                    $bestFitExponential->getStdevOfResiduals(),
305
                ],
306
                [
307
                    $bestFitExponential->getF(),
308
                    $bestFitExponential->getDFResiduals(),
309
                ],
310
                [
311
                    $bestFitExponential->getSSRegression(),
312
                    $bestFitExponential->getSSResiduals(),
313
                ],
314
            ];
315
        }
316
 
317
        return [
318
            $bestFitExponential->getSlope(),
319
            $bestFitExponential->getIntersect(),
320
        ];
321
    }
322
 
323
    /**
324
     * RSQ.
325
     *
326
     * Returns the square of the Pearson product moment correlation coefficient through data points
327
     *     in known_y's and known_x's.
328
     *
329
     * @param mixed[] $yValues Data Series Y
330
     * @param mixed[] $xValues Data Series X
331
     *
332
     * @return float|string The result, or a string containing an error
333
     */
334
    public static function RSQ(array $yValues, array $xValues)
335
    {
336
        try {
337
            self::checkTrendArrays($yValues, $xValues);
338
            self::validateTrendArrays($yValues, $xValues);
339
        } catch (Exception $e) {
340
            return $e->getMessage();
341
        }
342
 
343
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
344
 
345
        return $bestFitLinear->getGoodnessOfFit();
346
    }
347
 
348
    /**
349
     * SLOPE.
350
     *
351
     * Returns the slope of the linear regression line through data points in known_y's and known_x's.
352
     *
353
     * @param mixed[] $yValues Data Series Y
354
     * @param mixed[] $xValues Data Series X
355
     *
356
     * @return float|string The result, or a string containing an error
357
     */
358
    public static function SLOPE(array $yValues, array $xValues)
359
    {
360
        try {
361
            self::checkTrendArrays($yValues, $xValues);
362
            self::validateTrendArrays($yValues, $xValues);
363
        } catch (Exception $e) {
364
            return $e->getMessage();
365
        }
366
 
367
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
368
 
369
        return $bestFitLinear->getSlope();
370
    }
371
 
372
    /**
373
     * STEYX.
374
     *
375
     * Returns the standard error of the predicted y-value for each x in the regression.
376
     *
377
     * @param mixed[] $yValues Data Series Y
378
     * @param mixed[] $xValues Data Series X
379
     */
380
    public static function STEYX(array $yValues, array $xValues): float|string
381
    {
382
        try {
383
            self::checkTrendArrays($yValues, $xValues);
384
            self::validateTrendArrays($yValues, $xValues);
385
        } catch (Exception $e) {
386
            return $e->getMessage();
387
        }
388
 
389
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
390
 
391
        return $bestFitLinear->getStdevOfResiduals();
392
    }
393
 
394
    /**
395
     * TREND.
396
     *
397
     * Returns values along a linear Trend
398
     *
399
     * @param mixed[] $yValues Data Series Y
400
     * @param mixed[] $xValues Data Series X
401
     * @param mixed[] $newValues Values of X for which we want to find Y
402
     * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
403
     *
404
     * @return array<int, array<int, array<int, float>>>
405
     */
406
    public static function TREND(array $yValues, array $xValues = [], array $newValues = [], mixed $const = true): array
407
    {
408
        $yValues = Functions::flattenArray($yValues);
409
        $xValues = Functions::flattenArray($xValues);
410
        $newValues = Functions::flattenArray($newValues);
411
        $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
412
 
413
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
414
        if (empty($newValues)) {
415
            $newValues = $bestFitLinear->getXValues();
416
        }
417
 
418
        $returnArray = [];
419
        foreach ($newValues as $xValue) {
420
            $returnArray[0][] = [$bestFitLinear->getValueOfYForX($xValue)];
421
        }
422
 
423
        return $returnArray;
424
    }
425
}