Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
1 <?php 2 3 namespace PhpOffice\PhpSpreadsheet\Shared\Trend; 4 5 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 protected $goodnessOfFit = 1; 57 58 protected $stdevOfResiduals = 0; 59 60 protected $covariance = 0; 61 62 protected $correlation = 0; 63 64 protected $SSRegression = 0; 65 66 protected $SSResiduals = 0; 67 68 protected $DFResiduals = 0; 69 70 protected $f = 0; 71 72 protected $slope = 0; 73 74 protected $slopeSE = 0; 75 76 protected $intersect = 0; 77 78 protected $intersectSE = 0; 79 80 protected $xOffset = 0; 81 82 protected $yOffset = 0; 83 84 public function getError() 85 { 86 return $this->error; 87 } 88 89 public function getBestFitType() 90 { 91 return $this->bestFitType; 92 } 93 94 /** 95 * Return the Y-Value for a specified value of X. 96 * 97 * @param float $xValue X-Value 98 * 99 * @return bool Y-Value 100 */ 101 public function getValueOfYForX($xValue) 102 { 103 return false; 104 } 105 106 /** 107 * Return the X-Value for a specified value of Y. 108 * 109 * @param float $yValue Y-Value 110 * 111 * @return bool X-Value 112 */ 113 public function getValueOfXForY($yValue) 114 { 115 return false; 116 } 117 118 /** 119 * Return the original set of X-Values. 120 * 121 * @return float[] X-Values 122 */ 123 public function getXValues() 124 { 125 return $this->xValues; 126 } 127 128 /** 129 * Return the Equation of the best-fit line. 130 * 131 * @param int $dp Number of places of decimal precision to display 132 * 133 * @return bool 134 */ 135 public function getEquation($dp = 0) 136 { 137 return false; 138 } 139 140 /** 141 * Return the Slope of the line. 142 * 143 * @param int $dp Number of places of decimal precision to display 144 * 145 * @return float 146 */ 147 public function getSlope($dp = 0) 148 { 149 if ($dp != 0) { 150 return round($this->slope, $dp); 151 } 152 153 return $this->slope; 154 } 155 156 /** 157 * Return the standard error of the Slope. 158 * 159 * @param int $dp Number of places of decimal precision to display 160 * 161 * @return float 162 */ 163 public function getSlopeSE($dp = 0) 164 { 165 if ($dp != 0) { 166 return round($this->slopeSE, $dp); 167 } 168 169 return $this->slopeSE; 170 } 171 172 /** 173 * Return the Value of X where it intersects Y = 0. 174 * 175 * @param int $dp Number of places of decimal precision to display 176 * 177 * @return float 178 */ 179 public function getIntersect($dp = 0) 180 { 181 if ($dp != 0) { 182 return round($this->intersect, $dp); 183 } 184 185 return $this->intersect; 186 } 187 188 /** 189 * Return the standard error of the Intersect. 190 * 191 * @param int $dp Number of places of decimal precision to display 192 * 193 * @return float 194 */ 195 public function getIntersectSE($dp = 0) 196 { 197 if ($dp != 0) { 198 return round($this->intersectSE, $dp); 199 } 200 201 return $this->intersectSE; 202 } 203 204 /** 205 * Return the goodness of fit for this regression. 206 * 207 * @param int $dp Number of places of decimal precision to return 208 * 209 * @return float 210 */ 211 public function getGoodnessOfFit($dp = 0) 212 { 213 if ($dp != 0) { 214 return round($this->goodnessOfFit, $dp); 215 } 216 217 return $this->goodnessOfFit; 218 } 219 220 /** 221 * Return the goodness of fit for this regression. 222 * 223 * @param int $dp Number of places of decimal precision to return 224 * 225 * @return float 226 */ 227 public function getGoodnessOfFitPercent($dp = 0) 228 { 229 if ($dp != 0) { 230 return round($this->goodnessOfFit * 100, $dp); 231 } 232 233 return $this->goodnessOfFit * 100; 234 } 235 236 /** 237 * Return the standard deviation of the residuals for this regression. 238 * 239 * @param int $dp Number of places of decimal precision to return 240 * 241 * @return float 242 */ 243 public function getStdevOfResiduals($dp = 0) 244 { 245 if ($dp != 0) { 246 return round($this->stdevOfResiduals, $dp); 247 } 248 249 return $this->stdevOfResiduals; 250 } 251 252 /** 253 * @param int $dp Number of places of decimal precision to return 254 * 255 * @return float 256 */ 257 public function getSSRegression($dp = 0) 258 { 259 if ($dp != 0) { 260 return round($this->SSRegression, $dp); 261 } 262 263 return $this->SSRegression; 264 } 265 266 /** 267 * @param int $dp Number of places of decimal precision to return 268 * 269 * @return float 270 */ 271 public function getSSResiduals($dp = 0) 272 { 273 if ($dp != 0) { 274 return round($this->SSResiduals, $dp); 275 } 276 277 return $this->SSResiduals; 278 } 279 280 /** 281 * @param int $dp Number of places of decimal precision to return 282 * 283 * @return float 284 */ 285 public function getDFResiduals($dp = 0) 286 { 287 if ($dp != 0) { 288 return round($this->DFResiduals, $dp); 289 } 290 291 return $this->DFResiduals; 292 } 293 294 /** 295 * @param int $dp Number of places of decimal precision to return 296 * 297 * @return float 298 */ 299 public function getF($dp = 0) 300 { 301 if ($dp != 0) { 302 return round($this->f, $dp); 303 } 304 305 return $this->f; 306 } 307 308 /** 309 * @param int $dp Number of places of decimal precision to return 310 * 311 * @return float 312 */ 313 public function getCovariance($dp = 0) 314 { 315 if ($dp != 0) { 316 return round($this->covariance, $dp); 317 } 318 319 return $this->covariance; 320 } 321 322 /** 323 * @param int $dp Number of places of decimal precision to return 324 * 325 * @return float 326 */ 327 public function getCorrelation($dp = 0) 328 { 329 if ($dp != 0) { 330 return round($this->correlation, $dp); 331 } 332 333 return $this->correlation; 334 } 335 336 /** 337 * @return float[] 338 */ 339 public function getYBestFitValues() 340 { 341 return $this->yBestFitValues; 342 } 343 344 protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void 345 { 346 $SSres = $SScov = $SScor = $SStot = $SSsex = 0.0; 347 foreach ($this->xValues as $xKey => $xValue) { 348 $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue); 349 350 $SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY); 351 if ($const) { 352 $SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY); 353 } else { 354 $SStot += $this->yValues[$xKey] * $this->yValues[$xKey]; 355 } 356 $SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY); 357 if ($const) { 358 $SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX); 359 } else { 360 $SSsex += $this->xValues[$xKey] * $this->xValues[$xKey]; 361 } 362 } 363 364 $this->SSResiduals = $SSres; 365 $this->DFResiduals = $this->valueCount - 1 - $const; 366 367 if ($this->DFResiduals == 0.0) { 368 $this->stdevOfResiduals = 0.0; 369 } else { 370 $this->stdevOfResiduals = sqrt($SSres / $this->DFResiduals); 371 } 372 if (($SStot == 0.0) || ($SSres == $SStot)) { 373 $this->goodnessOfFit = 1; 374 } else { 375 $this->goodnessOfFit = 1 - ($SSres / $SStot); 376 } 377 378 $this->SSRegression = $this->goodnessOfFit * $SStot; 379 $this->covariance = $SScov / $this->valueCount; 380 $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - $sumX ** 2) * ($this->valueCount * $sumY2 - $sumY ** 2)); 381 $this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex); 382 $this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2)); 383 if ($this->SSResiduals != 0.0) { 384 if ($this->DFResiduals == 0.0) { 385 $this->f = 0.0; 386 } else { 387 $this->f = $this->SSRegression / ($this->SSResiduals / $this->DFResiduals); 388 } 389 } else { 390 if ($this->DFResiduals == 0.0) { 391 $this->f = 0.0; 392 } else { 393 $this->f = $this->SSRegression / $this->DFResiduals; 394 } 395 } 396 } 397 398 /** 399 * @param float[] $yValues 400 * @param float[] $xValues 401 * @param bool $const 402 */ 403 protected function leastSquareFit(array $yValues, array $xValues, $const): void 404 { 405 // calculate sums 406 $x_sum = array_sum($xValues); 407 $y_sum = array_sum($yValues); 408 $meanX = $x_sum / $this->valueCount; 409 $meanY = $y_sum / $this->valueCount; 410 $mBase = $mDivisor = $xx_sum = $xy_sum = $yy_sum = 0.0; 411 for ($i = 0; $i < $this->valueCount; ++$i) { 412 $xy_sum += $xValues[$i] * $yValues[$i]; 413 $xx_sum += $xValues[$i] * $xValues[$i]; 414 $yy_sum += $yValues[$i] * $yValues[$i]; 415 416 if ($const) { 417 $mBase += ($xValues[$i] - $meanX) * ($yValues[$i] - $meanY); 418 $mDivisor += ($xValues[$i] - $meanX) * ($xValues[$i] - $meanX); 419 } else { 420 $mBase += $xValues[$i] * $yValues[$i]; 421 $mDivisor += $xValues[$i] * $xValues[$i]; 422 } 423 } 424 425 // calculate slope 426 $this->slope = $mBase / $mDivisor; 427 428 // calculate intersect 429 if ($const) { 430 $this->intersect = $meanY - ($this->slope * $meanX); 431 } else { 432 $this->intersect = 0; 433 } 434 435 $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, $meanX, $meanY, $const); 436 } 437 438 /** 439 * Define the regression. 440 * 441 * @param float[] $yValues The set of Y-values for this regression 442 * @param float[] $xValues The set of X-values for this regression 443 * @param bool $const 444 */ 445 public function __construct($yValues, $xValues = [], $const = true) 446 { 447 // Calculate number of points 448 $nY = count($yValues); 449 $nX = count($xValues); 450 451 // Define X Values if necessary 452 if ($nX == 0) { 453 $xValues = range(1, $nY); 454 } elseif ($nY != $nX) { 455 // Ensure both arrays of points are the same size 456 $this->error = true; 457 } 458 459 $this->valueCount = $nY; 460 $this->xValues = $xValues; 461 $this->yValues = $yValues; 462 } 463 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body