See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 3 namespace PhpOffice\PhpSpreadsheet\Shared\Trend; 4 5 use PhpOffice\PhpSpreadsheet\Shared\JAMA\Matrix; 6 7 class PolynomialBestFit extends BestFit 8 { 9 /** 10 * Algorithm type to use for best-fit 11 * (Name of this Trend class). 12 * 13 * @var string 14 */ 15 protected $bestFitType = 'polynomial'; 16 17 /** 18 * Polynomial order. 19 * 20 * @var int 21 */ 22 protected $order = 0; 23 24 /** 25 * Return the order of this polynomial. 26 * 27 * @return int 28 */ 29 public function getOrder() 30 { 31 return $this->order; 32 } 33 34 /** 35 * Return the Y-Value for a specified value of X. 36 * 37 * @param float $xValue X-Value 38 * 39 * @return float Y-Value 40 */ 41 public function getValueOfYForX($xValue) 42 { 43 $retVal = $this->getIntersect(); 44 $slope = $this->getSlope(); 45 // @phpstan-ignore-next-line 46 foreach ($slope as $key => $value) { 47 if ($value != 0.0) { 48 $retVal += $value * $xValue ** ($key + 1); 49 } 50 } 51 52 return $retVal; 53 } 54 55 /** 56 * Return the X-Value for a specified value of Y. 57 * 58 * @param float $yValue Y-Value 59 * 60 * @return float X-Value 61 */ 62 public function getValueOfXForY($yValue) 63 { 64 return ($yValue - $this->getIntersect()) / $this->getSlope(); 65 } 66 67 /** 68 * Return the Equation of the best-fit line. 69 * 70 * @param int $dp Number of places of decimal precision to display 71 * 72 * @return string 73 */ 74 public function getEquation($dp = 0) 75 { 76 $slope = $this->getSlope($dp); 77 $intersect = $this->getIntersect($dp); 78 79 $equation = 'Y = ' . $intersect; 80 // @phpstan-ignore-next-line 81 foreach ($slope as $key => $value) { 82 if ($value != 0.0) { 83 $equation .= ' + ' . $value . ' * X'; 84 if ($key > 0) { 85 $equation .= '^' . ($key + 1); 86 } 87 } 88 } 89 90 return $equation; 91 } 92 93 /** 94 * Return the Slope of the line. 95 * 96 * @param int $dp Number of places of decimal precision to display 97 * 98 * @return float 99 */ 100 public function getSlope($dp = 0) 101 { 102 if ($dp != 0) { 103 $coefficients = []; 104 foreach ($this->slope as $coefficient) { 105 $coefficients[] = round($coefficient, $dp); 106 } 107 108 // @phpstan-ignore-next-line 109 return $coefficients; 110 } 111 112 return $this->slope; 113 } 114 115 public function getCoefficients($dp = 0) 116 { 117 // @phpstan-ignore-next-line 118 return array_merge([$this->getIntersect($dp)], $this->getSlope($dp)); 119 } 120 121 /** 122 * Execute the regression and calculate the goodness of fit for a set of X and Y data values. 123 * 124 * @param int $order Order of Polynomial for this regression 125 * @param float[] $yValues The set of Y-values for this regression 126 * @param float[] $xValues The set of X-values for this regression 127 */ 128 private function polynomialRegression($order, $yValues, $xValues): void 129 { 130 // calculate sums 131 $x_sum = array_sum($xValues); 132 $y_sum = array_sum($yValues); 133 $xx_sum = $xy_sum = $yy_sum = 0; 134 for ($i = 0; $i < $this->valueCount; ++$i) { 135 $xy_sum += $xValues[$i] * $yValues[$i]; 136 $xx_sum += $xValues[$i] * $xValues[$i]; 137 $yy_sum += $yValues[$i] * $yValues[$i]; 138 } 139 /* 140 * This routine uses logic from the PHP port of polyfit version 0.1 141 * written by Michael Bommarito and Paul Meagher 142 * 143 * The function fits a polynomial function of order $order through 144 * a series of x-y data points using least squares. 145 * 146 */ 147 $A = []; 148 $B = []; 149 for ($i = 0; $i < $this->valueCount; ++$i) { 150 for ($j = 0; $j <= $order; ++$j) { 151 $A[$i][$j] = $xValues[$i] ** $j; 152 } 153 } 154 for ($i = 0; $i < $this->valueCount; ++$i) { 155 $B[$i] = [$yValues[$i]]; 156 } 157 $matrixA = new Matrix($A); 158 $matrixB = new Matrix($B); 159 $C = $matrixA->solve($matrixB); 160 161 $coefficients = []; 162 for ($i = 0; $i < $C->getRowDimension(); ++$i) { 163 $r = $C->get($i, 0); 164 if (abs($r) <= 10 ** (-9)) { 165 $r = 0; 166 } 167 $coefficients[] = $r; 168 } 169 170 $this->intersect = array_shift($coefficients); 171 $this->slope = $coefficients; 172 173 $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0); 174 foreach ($this->xValues as $xKey => $xValue) { 175 $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue); 176 } 177 } 178 179 /** 180 * Define the regression and calculate the goodness of fit for a set of X and Y data values. 181 * 182 * @param int $order Order of Polynomial for this regression 183 * @param float[] $yValues The set of Y-values for this regression 184 * @param float[] $xValues The set of X-values for this regression 185 */ 186 public function __construct($order, $yValues, $xValues = []) 187 { 188 parent::__construct($yValues, $xValues); 189 190 if (!$this->error) { 191 if ($order < $this->valueCount) { 192 $this->bestFitType .= '_' . $order; 193 $this->order = $order; 194 $this->polynomialRegression($order, $yValues, $xValues); 195 if (($this->getGoodnessOfFit() < 0.0) || ($this->getGoodnessOfFit() > 1.0)) { 196 $this->error = true; 197 } 198 } else { 199 $this->error = true; 200 } 201 } 202 } 203 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body