Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
4
 
5
abstract class BestFit
6
{
7
    /**
8
     * Indicator flag for a calculation error.
9
     *
10
     * @var bool
11
     */
12
    protected $error = false;
13
 
14
    /**
15
     * Algorithm type to use for best-fit.
16
     *
17
     * @var string
18
     */
19
    protected $bestFitType = 'undetermined';
20
 
21
    /**
22
     * Number of entries in the sets of x- and y-value arrays.
23
     *
24
     * @var int
25
     */
26
    protected $valueCount = 0;
27
 
28
    /**
29
     * X-value dataseries of values.
30
     *
31
     * @var float[]
32
     */
33
    protected $xValues = [];
34
 
35
    /**
36
     * Y-value dataseries of values.
37
     *
38
     * @var float[]
39
     */
40
    protected $yValues = [];
41
 
42
    /**
43
     * Flag indicating whether values should be adjusted to Y=0.
44
     *
45
     * @var bool
46
     */
47
    protected $adjustToZero = false;
48
 
49
    /**
50
     * Y-value series of best-fit values.
51
     *
52
     * @var float[]
53
     */
54
    protected $yBestFitValues = [];
55
 
56
    /** @var float */
57
    protected $goodnessOfFit = 1;
58
 
59
    /** @var float */
60
    protected $stdevOfResiduals = 0;
61
 
62
    /** @var float */
63
    protected $covariance = 0;
64
 
65
    /** @var float */
66
    protected $correlation = 0;
67
 
68
    /** @var float */
69
    protected $SSRegression = 0;
70
 
71
    /** @var float */
72
    protected $SSResiduals = 0;
73
 
74
    /** @var float */
75
    protected $DFResiduals = 0;
76
 
77
    /** @var float */
78
    protected $f = 0;
79
 
80
    /** @var float */
81
    protected $slope = 0;
82
 
83
    /** @var float */
84
    protected $slopeSE = 0;
85
 
86
    /** @var float */
87
    protected $intersect = 0;
88
 
89
    /** @var float */
90
    protected $intersectSE = 0;
91
 
92
    /** @var float */
93
    protected $xOffset = 0;
94
 
95
    /** @var float */
96
    protected $yOffset = 0;
97
 
98
    /** @return bool */
99
    public function getError()
100
    {
101
        return $this->error;
102
    }
103
 
104
    /** @return string */
105
    public function getBestFitType()
106
    {
107
        return $this->bestFitType;
108
    }
109
 
110
    /**
111
     * Return the Y-Value for a specified value of X.
112
     *
113
     * @param float $xValue X-Value
114
     *
115
     * @return float Y-Value
116
     */
117
    abstract public function getValueOfYForX($xValue);
118
 
119
    /**
120
     * Return the X-Value for a specified value of Y.
121
     *
122
     * @param float $yValue Y-Value
123
     *
124
     * @return float X-Value
125
     */
126
    abstract public function getValueOfXForY($yValue);
127
 
128
    /**
129
     * Return the original set of X-Values.
130
     *
131
     * @return float[] X-Values
132
     */
133
    public function getXValues()
134
    {
135
        return $this->xValues;
136
    }
137
 
138
    /**
139
     * Return the Equation of the best-fit line.
140
     *
141
     * @param int $dp Number of places of decimal precision to display
142
     *
143
     * @return string
144
     */
145
    abstract public function getEquation($dp = 0);
146
 
147
    /**
148
     * Return the Slope of the line.
149
     *
150
     * @param int $dp Number of places of decimal precision to display
151
     *
152
     * @return float
153
     */
154
    public function getSlope($dp = 0)
155
    {
156
        if ($dp != 0) {
157
            return round($this->slope, $dp);
158
        }
159
 
160
        return $this->slope;
161
    }
162
 
163
    /**
164
     * Return the standard error of the Slope.
165
     *
166
     * @param int $dp Number of places of decimal precision to display
167
     *
168
     * @return float
169
     */
170
    public function getSlopeSE($dp = 0)
171
    {
172
        if ($dp != 0) {
173
            return round($this->slopeSE, $dp);
174
        }
175
 
176
        return $this->slopeSE;
177
    }
178
 
179
    /**
180
     * Return the Value of X where it intersects Y = 0.
181
     *
182
     * @param int $dp Number of places of decimal precision to display
183
     *
184
     * @return float
185
     */
186
    public function getIntersect($dp = 0)
187
    {
188
        if ($dp != 0) {
189
            return round($this->intersect, $dp);
190
        }
191
 
192
        return $this->intersect;
193
    }
194
 
195
    /**
196
     * Return the standard error of the Intersect.
197
     *
198
     * @param int $dp Number of places of decimal precision to display
199
     *
200
     * @return float
201
     */
202
    public function getIntersectSE($dp = 0)
203
    {
204
        if ($dp != 0) {
205
            return round($this->intersectSE, $dp);
206
        }
207
 
208
        return $this->intersectSE;
209
    }
210
 
211
    /**
212
     * Return the goodness of fit for this regression.
213
     *
214
     * @param int $dp Number of places of decimal precision to return
215
     *
216
     * @return float
217
     */
218
    public function getGoodnessOfFit($dp = 0)
219
    {
220
        if ($dp != 0) {
221
            return round($this->goodnessOfFit, $dp);
222
        }
223
 
224
        return $this->goodnessOfFit;
225
    }
226
 
227
    /**
228
     * Return the goodness of fit for this regression.
229
     *
230
     * @param int $dp Number of places of decimal precision to return
231
     *
232
     * @return float
233
     */
234
    public function getGoodnessOfFitPercent($dp = 0)
235
    {
236
        if ($dp != 0) {
237
            return round($this->goodnessOfFit * 100, $dp);
238
        }
239
 
240
        return $this->goodnessOfFit * 100;
241
    }
242
 
243
    /**
244
     * Return the standard deviation of the residuals for this regression.
245
     *
246
     * @param int $dp Number of places of decimal precision to return
247
     *
248
     * @return float
249
     */
250
    public function getStdevOfResiduals($dp = 0)
251
    {
252
        if ($dp != 0) {
253
            return round($this->stdevOfResiduals, $dp);
254
        }
255
 
256
        return $this->stdevOfResiduals;
257
    }
258
 
259
    /**
260
     * @param int $dp Number of places of decimal precision to return
261
     *
262
     * @return float
263
     */
264
    public function getSSRegression($dp = 0)
265
    {
266
        if ($dp != 0) {
267
            return round($this->SSRegression, $dp);
268
        }
269
 
270
        return $this->SSRegression;
271
    }
272
 
273
    /**
274
     * @param int $dp Number of places of decimal precision to return
275
     *
276
     * @return float
277
     */
278
    public function getSSResiduals($dp = 0)
279
    {
280
        if ($dp != 0) {
281
            return round($this->SSResiduals, $dp);
282
        }
283
 
284
        return $this->SSResiduals;
285
    }
286
 
287
    /**
288
     * @param int $dp Number of places of decimal precision to return
289
     *
290
     * @return float
291
     */
292
    public function getDFResiduals($dp = 0)
293
    {
294
        if ($dp != 0) {
295
            return round($this->DFResiduals, $dp);
296
        }
297
 
298
        return $this->DFResiduals;
299
    }
300
 
301
    /**
302
     * @param int $dp Number of places of decimal precision to return
303
     *
304
     * @return float
305
     */
306
    public function getF($dp = 0)
307
    {
308
        if ($dp != 0) {
309
            return round($this->f, $dp);
310
        }
311
 
312
        return $this->f;
313
    }
314
 
315
    /**
316
     * @param int $dp Number of places of decimal precision to return
317
     *
318
     * @return float
319
     */
320
    public function getCovariance($dp = 0)
321
    {
322
        if ($dp != 0) {
323
            return round($this->covariance, $dp);
324
        }
325
 
326
        return $this->covariance;
327
    }
328
 
329
    /**
330
     * @param int $dp Number of places of decimal precision to return
331
     *
332
     * @return float
333
     */
334
    public function getCorrelation($dp = 0)
335
    {
336
        if ($dp != 0) {
337
            return round($this->correlation, $dp);
338
        }
339
 
340
        return $this->correlation;
341
    }
342
 
343
    /**
344
     * @return float[]
345
     */
346
    public function getYBestFitValues()
347
    {
348
        return $this->yBestFitValues;
349
    }
350
 
351
    /** @var mixed */
352
    private static $scrutinizerZeroPointZero = 0.0;
353
 
354
    /**
355
     * @param mixed $x
356
     * @param mixed $y
357
     */
358
    private static function scrutinizerLooseCompare($x, $y): bool
359
    {
360
        return $x == $y;
361
    }
362
 
363
    /**
364
     * @param float $sumX
365
     * @param float $sumY
366
     * @param float $sumX2
367
     * @param float $sumY2
368
     * @param float $sumXY
369
     * @param float $meanX
370
     * @param float $meanY
371
     * @param bool|int $const
372
     */
373
    protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void
374
    {
375
        $SSres = $SScov = $SStot = $SSsex = 0.0;
376
        foreach ($this->xValues as $xKey => $xValue) {
377
            $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
378
 
379
            $SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY);
380
            if ($const === true) {
381
                $SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY);
382
            } else {
383
                $SStot += $this->yValues[$xKey] * $this->yValues[$xKey];
384
            }
385
            $SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY);
386
            if ($const === true) {
387
                $SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX);
388
            } else {
389
                $SSsex += $this->xValues[$xKey] * $this->xValues[$xKey];
390
            }
391
        }
392
 
393
        $this->SSResiduals = $SSres;
394
        $this->DFResiduals = $this->valueCount - 1 - ($const === true ? 1 : 0);
395
 
396
        if ($this->DFResiduals == 0.0) {
397
            $this->stdevOfResiduals = 0.0;
398
        } else {
399
            $this->stdevOfResiduals = sqrt($SSres / $this->DFResiduals);
400
        }
401
        // Scrutinizer thinks $SSres == $SStot is always true. It is wrong.
402
        if ($SStot == self::$scrutinizerZeroPointZero || self::scrutinizerLooseCompare($SSres, $SStot)) {
403
            $this->goodnessOfFit = 1;
404
        } else {
405
            $this->goodnessOfFit = 1 - ($SSres / $SStot);
406
        }
407
 
408
        $this->SSRegression = $this->goodnessOfFit * $SStot;
409
        $this->covariance = $SScov / $this->valueCount;
410
        $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - $sumX ** 2) * ($this->valueCount * $sumY2 - $sumY ** 2));
411
        $this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex);
412
        $this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2));
413
        if ($this->SSResiduals != 0.0) {
414
            if ($this->DFResiduals == 0.0) {
415
                $this->f = 0.0;
416
            } else {
417
                $this->f = $this->SSRegression / ($this->SSResiduals / $this->DFResiduals);
418
            }
419
        } else {
420
            if ($this->DFResiduals == 0.0) {
421
                $this->f = 0.0;
422
            } else {
423
                $this->f = $this->SSRegression / $this->DFResiduals;
424
            }
425
        }
426
    }
427
 
428
    /** @return float|int */
429
    private function sumSquares(array $values)
430
    {
431
        return array_sum(
432
            array_map(
433
                function ($value) {
434
                    return $value ** 2;
435
                },
436
                $values
437
            )
438
        );
439
    }
440
 
441
    /**
442
     * @param float[] $yValues
443
     * @param float[] $xValues
444
     */
445
    protected function leastSquareFit(array $yValues, array $xValues, bool $const): void
446
    {
447
        // calculate sums
448
        $sumValuesX = array_sum($xValues);
449
        $sumValuesY = array_sum($yValues);
450
        $meanValueX = $sumValuesX / $this->valueCount;
451
        $meanValueY = $sumValuesY / $this->valueCount;
452
        $sumSquaresX = $this->sumSquares($xValues);
453
        $sumSquaresY = $this->sumSquares($yValues);
454
        $mBase = $mDivisor = 0.0;
455
        $xy_sum = 0.0;
456
        for ($i = 0; $i < $this->valueCount; ++$i) {
457
            $xy_sum += $xValues[$i] * $yValues[$i];
458
 
459
            if ($const === true) {
460
                $mBase += ($xValues[$i] - $meanValueX) * ($yValues[$i] - $meanValueY);
461
                $mDivisor += ($xValues[$i] - $meanValueX) * ($xValues[$i] - $meanValueX);
462
            } else {
463
                $mBase += $xValues[$i] * $yValues[$i];
464
                $mDivisor += $xValues[$i] * $xValues[$i];
465
            }
466
        }
467
 
468
        // calculate slope
469
        $this->slope = $mBase / $mDivisor;
470
 
471
        // calculate intersect
472
        $this->intersect = ($const === true) ? $meanValueY - ($this->slope * $meanValueX) : 0.0;
473
 
474
        $this->calculateGoodnessOfFit($sumValuesX, $sumValuesY, $sumSquaresX, $sumSquaresY, $xy_sum, $meanValueX, $meanValueY, $const);
475
    }
476
 
477
    /**
478
     * Define the regression.
479
     *
480
     * @param float[] $yValues The set of Y-values for this regression
481
     * @param float[] $xValues The set of X-values for this regression
482
     */
483
    public function __construct($yValues, $xValues = [])
484
    {
485
        //    Calculate number of points
486
        $yValueCount = count($yValues);
487
        $xValueCount = count($xValues);
488
 
489
        //    Define X Values if necessary
490
        if ($xValueCount === 0) {
491
            $xValues = range(1, $yValueCount);
492
        } elseif ($yValueCount !== $xValueCount) {
493
            //    Ensure both arrays of points are the same size
494
            $this->error = true;
495
        }
496
 
497
        $this->valueCount = $yValueCount;
498
        $this->xValues = $xValues;
499
        $this->yValues = $yValues;
500
    }
501
}