PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Shared / Trend / BestFit.php

BestFit.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Shared/Trend/BestFit.php

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