From c866be3c7aedc5a04410c3eb90aaf6abee9797eb Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Tue, 22 Mar 2016 16:44:56 +0100 Subject: [PATCH] #401 : Support for namespaces --- .../Calculation/Statistical.php | 4 +- src/PhpSpreadsheet/Reader/Excel5.php | 8 +- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 427 ++++++++++++++++++ .../Shared/Trend/ExponentialBestFit.php | 138 ++++++ .../Shared/Trend/LinearBestFit.php | 102 +++++ .../Shared/Trend/LogarithmicBestFit.php | 110 +++++ .../Shared/Trend/PolynomialBestFit.php | 221 +++++++++ .../Shared/Trend/PowerBestFit.php | 138 ++++++ src/PhpSpreadsheet/Shared/Trend/Trend.php | 143 ++++++ 9 files changed, 1285 insertions(+), 6 deletions(-) create mode 100644 src/PhpSpreadsheet/Shared/Trend/BestFit.php create mode 100644 src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php create mode 100644 src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php create mode 100644 src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php create mode 100644 src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php create mode 100644 src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php create mode 100644 src/PhpSpreadsheet/Shared/Trend/Trend.php diff --git a/src/PhpSpreadsheet/Calculation/Statistical.php b/src/PhpSpreadsheet/Calculation/Statistical.php index 0ba90b13..6245b3fc 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical.php +++ b/src/PhpSpreadsheet/Calculation/Statistical.php @@ -1765,7 +1765,7 @@ class Statistical /** * GROWTH * - * Returns values along a predicted emponential trend + * Returns values along a predicted emponential Trend * * @param array of mixed Data Series Y * @param array of mixed Data Series X @@ -3404,7 +3404,7 @@ class Statistical /** * TREND * - * Returns values along a linear trend + * Returns values along a linear Trend * * @param array of mixed Data Series Y * @param array of mixed Data Series X diff --git a/src/PhpSpreadsheet/Reader/Excel5.php b/src/PhpSpreadsheet/Reader/Excel5.php index df4e3ba2..22d818ec 100644 --- a/src/PhpSpreadsheet/Reader/Excel5.php +++ b/src/PhpSpreadsheet/Reader/Excel5.php @@ -63,8 +63,8 @@ class Excel5 extends BaseReader implements IReader // ParseXL definitions const XLS_BIFF8 = 0x0600; const XLS_BIFF7 = 0x0500; - const XLS_WorkbookGlobals = 0x0005; - const XLS_Worksheet = 0x0010; + const XLS_WORKBOOKGLOBALS = 0x0005; + const XLS_WORKSHEET = 0x0010; // record identifiers const XLS_TYPE_FORMULA = 0x0006; @@ -1688,14 +1688,14 @@ class Excel5 extends BaseReader implements IReader $substreamType = self::getInt2d($recordData, 2); switch ($substreamType) { - case self::XLS_WorkbookGlobals: + case self::XLS_WORKBOOKGLOBALS: $version = self::getInt2d($recordData, 0); if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) { throw new Exception('Cannot read this Excel file. Version is too old.'); } $this->version = $version; break; - case self::XLS_Worksheet: + case self::XLS_WORKSHEET: // do not use this version information for anything // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream break; diff --git a/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/src/PhpSpreadsheet/Shared/Trend/BestFit.php new file mode 100644 index 00000000..a2156d4d --- /dev/null +++ b/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -0,0 +1,427 @@ +error; + } + + + public function getBestFitType() + { + return $this->bestFitType; + } + + /** + * Return the Y-Value for a specified value of X + * + * @param float $xValue X-Value + * @return float Y-Value + */ + public function getValueOfYForX($xValue) + { + return false; + } + + /** + * Return the X-Value for a specified value of Y + * + * @param float $yValue Y-Value + * @return float X-Value + */ + public function getValueOfXForY($yValue) + { + return false; + } + + /** + * Return the original set of X-Values + * + * @return float[] X-Values + */ + public function getXValues() + { + return $this->xValues; + } + + /** + * Return the Equation of the best-fit line + * + * @param int $dp Number of places of decimal precision to display + * @return string + */ + public function getEquation($dp = 0) + { + return false; + } + + /** + * Return the Slope of the line + * + * @param int $dp Number of places of decimal precision to display + * @return string + */ + public function getSlope($dp = 0) + { + if ($dp != 0) { + return round($this->slope, $dp); + } + return $this->slope; + } + + /** + * Return the standard error of the Slope + * + * @param int $dp Number of places of decimal precision to display + * @return string + */ + public function getSlopeSE($dp = 0) + { + if ($dp != 0) { + return round($this->slopeSE, $dp); + } + return $this->slopeSE; + } + + /** + * Return the Value of X where it intersects Y = 0 + * + * @param int $dp Number of places of decimal precision to display + * @return string + */ + public function getIntersect($dp = 0) + { + if ($dp != 0) { + return round($this->intersect, $dp); + } + return $this->intersect; + } + + /** + * Return the standard error of the Intersect + * + * @param int $dp Number of places of decimal precision to display + * @return string + */ + public function getIntersectSE($dp = 0) + { + if ($dp != 0) { + return round($this->intersectSE, $dp); + } + return $this->intersectSE; + } + + /** + * Return the goodness of fit for this regression + * + * @param int $dp Number of places of decimal precision to return + * @return float + */ + public function getGoodnessOfFit($dp = 0) + { + if ($dp != 0) { + return round($this->goodnessOfFit, $dp); + } + return $this->goodnessOfFit; + } + + public function getGoodnessOfFitPercent($dp = 0) + { + if ($dp != 0) { + return round($this->goodnessOfFit * 100, $dp); + } + return $this->goodnessOfFit * 100; + } + + /** + * Return the standard deviation of the residuals for this regression + * + * @param int $dp Number of places of decimal precision to return + * @return float + */ + public function getStdevOfResiduals($dp = 0) + { + if ($dp != 0) { + return round($this->stdevOfResiduals, $dp); + } + return $this->stdevOfResiduals; + } + + public function getSSRegression($dp = 0) + { + if ($dp != 0) { + return round($this->SSRegression, $dp); + } + return $this->SSRegression; + } + + public function getSSResiduals($dp = 0) + { + if ($dp != 0) { + return round($this->SSResiduals, $dp); + } + return $this->SSResiduals; + } + + public function getDFResiduals($dp = 0) + { + if ($dp != 0) { + return round($this->DFResiduals, $dp); + } + return $this->DFResiduals; + } + + public function getF($dp = 0) + { + if ($dp != 0) { + return round($this->f, $dp); + } + return $this->f; + } + + public function getCovariance($dp = 0) + { + if ($dp != 0) { + return round($this->covariance, $dp); + } + return $this->covariance; + } + + public function getCorrelation($dp = 0) + { + if ($dp != 0) { + return round($this->correlation, $dp); + } + return $this->correlation; + } + + public function getYBestFitValues() + { + return $this->yBestFitValues; + } + + protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const) + { + $SSres = $SScov = $SScor = $SStot = $SSsex = 0.0; + foreach ($this->xValues as $xKey => $xValue) { + $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue); + + $SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY); + if ($const) { + $SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY); + } else { + $SStot += $this->yValues[$xKey] * $this->yValues[$xKey]; + } + $SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY); + if ($const) { + $SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX); + } else { + $SSsex += $this->xValues[$xKey] * $this->xValues[$xKey]; + } + } + + $this->SSResiduals = $SSres; + $this->DFResiduals = $this->valueCount - 1 - $const; + + if ($this->DFResiduals == 0.0) { + $this->stdevOfResiduals = 0.0; + } else { + $this->stdevOfResiduals = sqrt($SSres / $this->DFResiduals); + } + if (($SStot == 0.0) || ($SSres == $SStot)) { + $this->goodnessOfFit = 1; + } else { + $this->goodnessOfFit = 1 - ($SSres / $SStot); + } + + $this->SSRegression = $this->goodnessOfFit * $SStot; + $this->covariance = $SScov / $this->valueCount; + $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - pow($sumX, 2)) * ($this->valueCount * $sumY2 - pow($sumY, 2))); + $this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex); + $this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2)); + if ($this->SSResiduals != 0.0) { + if ($this->DFResiduals == 0.0) { + $this->f = 0.0; + } else { + $this->f = $this->SSRegression / ($this->SSResiduals / $this->DFResiduals); + } + } else { + if ($this->DFResiduals == 0.0) { + $this->f = 0.0; + } else { + $this->f = $this->SSRegression / $this->DFResiduals; + } + } + } + + protected function leastSquareFit($yValues, $xValues, $const) + { + // calculate sums + $x_sum = array_sum($xValues); + $y_sum = array_sum($yValues); + $meanX = $x_sum / $this->valueCount; + $meanY = $y_sum / $this->valueCount; + $mBase = $mDivisor = $xx_sum = $xy_sum = $yy_sum = 0.0; + for ($i = 0; $i < $this->valueCount; ++$i) { + $xy_sum += $xValues[$i] * $yValues[$i]; + $xx_sum += $xValues[$i] * $xValues[$i]; + $yy_sum += $yValues[$i] * $yValues[$i]; + + if ($const) { + $mBase += ($xValues[$i] - $meanX) * ($yValues[$i] - $meanY); + $mDivisor += ($xValues[$i] - $meanX) * ($xValues[$i] - $meanX); + } else { + $mBase += $xValues[$i] * $yValues[$i]; + $mDivisor += $xValues[$i] * $xValues[$i]; + } + } + + // calculate slope +// $this->slope = (($this->valueCount * $xy_sum) - ($x_sum * $y_sum)) / (($this->valueCount * $xx_sum) - ($x_sum * $x_sum)); + $this->slope = $mBase / $mDivisor; + + // calculate intersect +// $this->intersect = ($y_sum - ($this->slope * $x_sum)) / $this->valueCount; + if ($const) { + $this->intersect = $meanY - ($this->slope * $meanX); + } else { + $this->intersect = 0; + } + + $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, $meanX, $meanY, $const); + } + + /** + * Define the regression + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + public function __construct($yValues, $xValues = array(), $const = true) + { + // Calculate number of points + $nY = count($yValues); + $nX = count($xValues); + + // Define X Values if necessary + if ($nX == 0) { + $xValues = range(1, $nY); + $nX = $nY; + } elseif ($nY != $nX) { + // Ensure both arrays of points are the same size + $this->error = true; + return false; + } + + $this->valueCount = $nY; + $this->xValues = $xValues; + $this->yValues = $yValues; + } +} diff --git a/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php b/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php new file mode 100644 index 00000000..5354e23c --- /dev/null +++ b/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php @@ -0,0 +1,138 @@ +getIntersect() * pow($this->getSlope(), ($xValue - $this->xOffset)); + } + + /** + * Return the X-Value for a specified value of Y + * + * @param float $yValue Y-Value + * @return float X-Value + **/ + public function getValueOfXForY($yValue) + { + return log(($yValue + $this->yOffset) / $this->getIntersect()) / log($this->getSlope()); + } + + /** + * Return the Equation of the best-fit line + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getEquation($dp = 0) + { + $slope = $this->getSlope($dp); + $intersect = $this->getIntersect($dp); + + return 'Y = ' . $intersect . ' * ' . $slope . '^X'; + } + + /** + * Return the Slope of the line + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getSlope($dp = 0) + { + if ($dp != 0) { + return round(exp($this->_slope), $dp); + } + return exp($this->_slope); + } + + /** + * Return the Value of X where it intersects Y = 0 + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getIntersect($dp = 0) + { + if ($dp != 0) { + return round(exp($this->intersect), $dp); + } + return exp($this->intersect); + } + + /** + * Execute the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + private function exponentialRegression($yValues, $xValues, $const) + { + foreach ($yValues as &$value) { + if ($value < 0.0) { + $value = 0 - log(abs($value)); + } elseif ($value > 0.0) { + $value = log($value); + } + } + unset($value); + + $this->leastSquareFit($yValues, $xValues, $const); + } + + /** + * Define the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + public function __construct($yValues, $xValues = array(), $const = true) + { + if (parent::__construct($yValues, $xValues) !== false) { + $this->exponentialRegression($yValues, $xValues, $const); + } + } +} diff --git a/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php b/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php new file mode 100644 index 00000000..58773a52 --- /dev/null +++ b/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php @@ -0,0 +1,102 @@ +getIntersect() + $this->getSlope() * $xValue; + } + + /** + * Return the X-Value for a specified value of Y + * + * @param float $yValue Y-Value + * @return float X-Value + **/ + public function getValueOfXForY($yValue) + { + return ($yValue - $this->getIntersect()) / $this->getSlope(); + } + + + /** + * Return the Equation of the best-fit line + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getEquation($dp = 0) + { + $slope = $this->getSlope($dp); + $intersect = $this->getIntersect($dp); + + return 'Y = ' . $intersect . ' + ' . $slope . ' * X'; + } + + /** + * Execute the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + private function linearRegression($yValues, $xValues, $const) + { + $this->leastSquareFit($yValues, $xValues, $const); + } + + /** + * Define the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + public function __construct($yValues, $xValues = array(), $const = true) + { + if (parent::__construct($yValues, $xValues) !== false) { + $this->linearRegression($yValues, $xValues, $const); + } + } +} diff --git a/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php b/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php new file mode 100644 index 00000000..19abab90 --- /dev/null +++ b/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php @@ -0,0 +1,110 @@ +getIntersect() + $this->getSlope() * log($xValue - $this->xOffset); + } + + /** + * Return the X-Value for a specified value of Y + * + * @param float $yValue Y-Value + * @return float X-Value + **/ + public function getValueOfXForY($yValue) + { + return exp(($yValue - $this->getIntersect()) / $this->getSlope()); + } + + /** + * Return the Equation of the best-fit line + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getEquation($dp = 0) + { + $slope = $this->getSlope($dp); + $intersect = $this->getIntersect($dp); + + return 'Y = '.$intersect.' + '.$slope.' * log(X)'; + } + + /** + * Execute the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + private function logarithmicRegression($yValues, $xValues, $const) + { + foreach ($xValues as &$value) { + if ($value < 0.0) { + $value = 0 - log(abs($value)); + } elseif ($value > 0.0) { + $value = log($value); + } + } + unset($value); + + $this->leastSquareFit($yValues, $xValues, $const); + } + + /** + * Define the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + public function __construct($yValues, $xValues = array(), $const = true) + { + if (parent::__construct($yValues, $xValues) !== false) { + $this->logarithmicRegression($yValues, $xValues, $const); + } + } +} diff --git a/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php b/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php new file mode 100644 index 00000000..af24626a --- /dev/null +++ b/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php @@ -0,0 +1,221 @@ +order; + } + + + /** + * Return the Y-Value for a specified value of X + * + * @param float $xValue X-Value + * @return float Y-Value + **/ + public function getValueOfYForX($xValue) + { + $retVal = $this->getIntersect(); + $slope = $this->getSlope(); + foreach ($slope as $key => $value) { + if ($value != 0.0) { + $retVal += $value * pow($xValue, $key + 1); + } + } + return $retVal; + } + + + /** + * Return the X-Value for a specified value of Y + * + * @param float $yValue Y-Value + * @return float X-Value + **/ + public function getValueOfXForY($yValue) + { + return ($yValue - $this->getIntersect()) / $this->getSlope(); + } + + + /** + * Return the Equation of the best-fit line + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getEquation($dp = 0) + { + $slope = $this->getSlope($dp); + $intersect = $this->getIntersect($dp); + + $equation = 'Y = ' . $intersect; + foreach ($slope as $key => $value) { + if ($value != 0.0) { + $equation .= ' + ' . $value . ' * X'; + if ($key > 0) { + $equation .= '^' . ($key + 1); + } + } + } + return $equation; + } + + + /** + * Return the Slope of the line + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getSlope($dp = 0) + { + if ($dp != 0) { + $coefficients = array(); + foreach ($this->_slope as $coefficient) { + $coefficients[] = round($coefficient, $dp); + } + return $coefficients; + } + return $this->_slope; + } + + + public function getCoefficients($dp = 0) + { + return array_merge(array($this->getIntersect($dp)), $this->getSlope($dp)); + } + + + /** + * Execute the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param int $order Order of Polynomial for this regression + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + private function polynomialRegression($order, $yValues, $xValues, $const) + { + // calculate sums + $x_sum = array_sum($xValues); + $y_sum = array_sum($yValues); + $xx_sum = $xy_sum = 0; + for ($i = 0; $i < $this->valueCount; ++$i) { + $xy_sum += $xValues[$i] * $yValues[$i]; + $xx_sum += $xValues[$i] * $xValues[$i]; + $yy_sum += $yValues[$i] * $yValues[$i]; + } + /* + * This routine uses logic from the PHP port of polyfit version 0.1 + * written by Michael Bommarito and Paul Meagher + * + * The function fits a polynomial function of order $order through + * a series of x-y data points using least squares. + * + */ + for ($i = 0; $i < $this->valueCount; ++$i) { + for ($j = 0; $j <= $order; ++$j) { + $A[$i][$j] = pow($xValues[$i], $j); + } + } + for ($i=0; $i < $this->valueCount; ++$i) { + $B[$i] = array($yValues[$i]); + } + $matrixA = new Matrix($A); + $matrixB = new Matrix($B); + $C = $matrixA->solve($matrixB); + + $coefficients = array(); + for ($i = 0; $i < $C->m; ++$i) { + $r = $C->get($i, 0); + if (abs($r) <= pow(10, -9)) { + $r = 0; + } + $coefficients[] = $r; + } + + $this->intersect = array_shift($coefficients); + $this->_slope = $coefficients; + + $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum); + foreach ($this->xValues as $xKey => $xValue) { + $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue); + } + } + + + /** + * Define the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param int $order Order of Polynomial for this regression + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + public function __construct($order, $yValues, $xValues = array(), $const = true) + { + if (parent::__construct($yValues, $xValues) !== false) { + if ($order < $this->valueCount) { + $this->bestFitType .= '_'.$order; + $this->order = $order; + $this->polynomialRegression($order, $yValues, $xValues, $const); + if (($this->getGoodnessOfFit() < 0.0) || ($this->getGoodnessOfFit() > 1.0)) { + $this->_error = true; + } + } else { + $this->_error = true; + } + } + } +} diff --git a/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php b/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php new file mode 100644 index 00000000..75cffe92 --- /dev/null +++ b/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php @@ -0,0 +1,138 @@ +getIntersect() * pow(($xValue - $this->xOffset), $this->getSlope()); + } + + + /** + * Return the X-Value for a specified value of Y + * + * @param float $yValue Y-Value + * @return float X-Value + **/ + public function getValueOfXForY($yValue) + { + return pow((($yValue + $this->yOffset) / $this->getIntersect()), (1 / $this->getSlope())); + } + + + /** + * Return the Equation of the best-fit line + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getEquation($dp = 0) + { + $slope = $this->getSlope($dp); + $intersect = $this->getIntersect($dp); + + return 'Y = ' . $intersect . ' * X^' . $slope; + } + + + /** + * Return the Value of X where it intersects Y = 0 + * + * @param int $dp Number of places of decimal precision to display + * @return string + **/ + public function getIntersect($dp = 0) + { + if ($dp != 0) { + return round(exp($this->intersect), $dp); + } + return exp($this->intersect); + } + + + /** + * Execute the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + private function powerRegression($yValues, $xValues, $const) + { + foreach ($xValues as &$value) { + if ($value < 0.0) { + $value = 0 - log(abs($value)); + } elseif ($value > 0.0) { + $value = log($value); + } + } + unset($value); + foreach ($yValues as &$value) { + if ($value < 0.0) { + $value = 0 - log(abs($value)); + } elseif ($value > 0.0) { + $value = log($value); + } + } + unset($value); + + $this->leastSquareFit($yValues, $xValues, $const); + } + + + /** + * Define the regression and calculate the goodness of fit for a set of X and Y data values + * + * @param float[] $yValues The set of Y-values for this regression + * @param float[] $xValues The set of X-values for this regression + * @param boolean $const + */ + public function __construct($yValues, $xValues = array(), $const = true) + { + if (parent::__construct($yValues, $xValues) !== false) { + $this->powerRegression($yValues, $xValues, $const); + } + } +} diff --git a/src/PhpSpreadsheet/Shared/Trend/Trend.php b/src/PhpSpreadsheet/Shared/Trend/Trend.php new file mode 100644 index 00000000..3299a290 --- /dev/null +++ b/src/PhpSpreadsheet/Shared/Trend/Trend.php @@ -0,0 +1,143 @@ +getGoodnessOfFit(); + } + if ($trendType != self::TREND_BEST_FIT_NO_POLY) { + foreach (self::$trendTypePolynomialOrders as $trendMethod) { + $order = substr($trendMethod, -1); + $bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues, $const); + if ($bestFit[$trendMethod]->getError()) { + unset($bestFit[$trendMethod]); + } else { + $bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit(); + } + } + } + // Determine which of our Trend lines is the best fit, and then we return the instance of that Trend class + arsort($bestFitValue); + $bestFitType = key($bestFitValue); + return $bestFit[$bestFitType]; + default: + return false; + } + } +}