Trend unit tests (#1899)
- Move TREND() functions into the Statistical Trends class - Unit tests for TREND() - Create Confidence class for Statistical Confidence functions
This commit is contained in:
parent
a79a4ddbab
commit
2d8c8c8ecf
|
|
@ -226,7 +226,7 @@ class Trends
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$bestFitLinear->getSlopeSE(),
|
$bestFitLinear->getSlopeSE(),
|
||||||
$bestFitLinear->getIntersectSE(),
|
($const === false) ? Functions::NA() : $bestFitLinear->getIntersectSE(),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$bestFitLinear->getGoodnessOfFit(),
|
$bestFitLinear->getGoodnessOfFit(),
|
||||||
|
|
@ -293,7 +293,7 @@ class Trends
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$bestFitExponential->getSlopeSE(),
|
$bestFitExponential->getSlopeSE(),
|
||||||
$bestFitExponential->getIntersectSE(),
|
($const === false) ? Functions::NA() : $bestFitExponential->getIntersectSE(),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$bestFitExponential->getGoodnessOfFit(),
|
$bestFitExponential->getGoodnessOfFit(),
|
||||||
|
|
|
||||||
|
|
@ -348,13 +348,13 @@ class BestFit
|
||||||
$bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
|
$bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
|
||||||
|
|
||||||
$SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY);
|
$SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY);
|
||||||
if ($const) {
|
if ($const === true) {
|
||||||
$SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY);
|
$SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY);
|
||||||
} else {
|
} else {
|
||||||
$SStot += $this->yValues[$xKey] * $this->yValues[$xKey];
|
$SStot += $this->yValues[$xKey] * $this->yValues[$xKey];
|
||||||
}
|
}
|
||||||
$SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY);
|
$SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY);
|
||||||
if ($const) {
|
if ($const === true) {
|
||||||
$SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX);
|
$SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX);
|
||||||
} else {
|
} else {
|
||||||
$SSsex += $this->xValues[$xKey] * $this->xValues[$xKey];
|
$SSsex += $this->xValues[$xKey] * $this->xValues[$xKey];
|
||||||
|
|
@ -362,7 +362,7 @@ class BestFit
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->SSResiduals = $SSres;
|
$this->SSResiduals = $SSres;
|
||||||
$this->DFResiduals = $this->valueCount - 1 - $const;
|
$this->DFResiduals = $this->valueCount - 1 - ($const === true ? 1 : 0);
|
||||||
|
|
||||||
if ($this->DFResiduals == 0.0) {
|
if ($this->DFResiduals == 0.0) {
|
||||||
$this->stdevOfResiduals = 0.0;
|
$this->stdevOfResiduals = 0.0;
|
||||||
|
|
@ -395,27 +395,39 @@ class BestFit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sumSquares(array $values)
|
||||||
|
{
|
||||||
|
return array_sum(
|
||||||
|
array_map(
|
||||||
|
function ($value) {
|
||||||
|
return $value ** 2;
|
||||||
|
},
|
||||||
|
$values
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param float[] $yValues
|
* @param float[] $yValues
|
||||||
* @param float[] $xValues
|
* @param float[] $xValues
|
||||||
* @param bool $const
|
|
||||||
*/
|
*/
|
||||||
protected function leastSquareFit(array $yValues, array $xValues, $const): void
|
protected function leastSquareFit(array $yValues, array $xValues, bool $const): void
|
||||||
{
|
{
|
||||||
// calculate sums
|
// calculate sums
|
||||||
$x_sum = array_sum($xValues);
|
$sumValuesX = array_sum($xValues);
|
||||||
$y_sum = array_sum($yValues);
|
$sumValuesY = array_sum($yValues);
|
||||||
$meanX = $x_sum / $this->valueCount;
|
$meanValueX = $sumValuesX / $this->valueCount;
|
||||||
$meanY = $y_sum / $this->valueCount;
|
$meanValueY = $sumValuesY / $this->valueCount;
|
||||||
$mBase = $mDivisor = $xx_sum = $xy_sum = $yy_sum = 0.0;
|
$sumSquaresX = $this->sumSquares($xValues);
|
||||||
|
$sumSquaresY = $this->sumSquares($yValues);
|
||||||
|
$mBase = $mDivisor = 0.0;
|
||||||
|
$xy_sum = 0.0;
|
||||||
for ($i = 0; $i < $this->valueCount; ++$i) {
|
for ($i = 0; $i < $this->valueCount; ++$i) {
|
||||||
$xy_sum += $xValues[$i] * $yValues[$i];
|
$xy_sum += $xValues[$i] * $yValues[$i];
|
||||||
$xx_sum += $xValues[$i] * $xValues[$i];
|
|
||||||
$yy_sum += $yValues[$i] * $yValues[$i];
|
|
||||||
|
|
||||||
if ($const) {
|
if ($const === true) {
|
||||||
$mBase += ($xValues[$i] - $meanX) * ($yValues[$i] - $meanY);
|
$mBase += ($xValues[$i] - $meanValueX) * ($yValues[$i] - $meanValueY);
|
||||||
$mDivisor += ($xValues[$i] - $meanX) * ($xValues[$i] - $meanX);
|
$mDivisor += ($xValues[$i] - $meanValueX) * ($xValues[$i] - $meanValueX);
|
||||||
} else {
|
} else {
|
||||||
$mBase += $xValues[$i] * $yValues[$i];
|
$mBase += $xValues[$i] * $yValues[$i];
|
||||||
$mDivisor += $xValues[$i] * $xValues[$i];
|
$mDivisor += $xValues[$i] * $xValues[$i];
|
||||||
|
|
@ -426,13 +438,9 @@ class BestFit
|
||||||
$this->slope = $mBase / $mDivisor;
|
$this->slope = $mBase / $mDivisor;
|
||||||
|
|
||||||
// calculate intersect
|
// calculate intersect
|
||||||
if ($const) {
|
$this->intersect = ($const === true) ? $meanValueY - ($this->slope * $meanValueX) : 0.0;
|
||||||
$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);
|
$this->calculateGoodnessOfFit($sumValuesX, $sumValuesY, $sumSquaresX, $sumSquaresY, $xy_sum, $meanValueX, $meanValueY, $const);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -440,23 +448,22 @@ class BestFit
|
||||||
*
|
*
|
||||||
* @param float[] $yValues The set of Y-values 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 float[] $xValues The set of X-values for this regression
|
||||||
* @param bool $const
|
|
||||||
*/
|
*/
|
||||||
public function __construct($yValues, $xValues = [], $const = true)
|
public function __construct($yValues, $xValues = [])
|
||||||
{
|
{
|
||||||
// Calculate number of points
|
// Calculate number of points
|
||||||
$nY = count($yValues);
|
$yValueCount = count($yValues);
|
||||||
$nX = count($xValues);
|
$xValueCount = count($xValues);
|
||||||
|
|
||||||
// Define X Values if necessary
|
// Define X Values if necessary
|
||||||
if ($nX == 0) {
|
if ($xValueCount === 0) {
|
||||||
$xValues = range(1, $nY);
|
$xValues = range(1, $yValueCount);
|
||||||
} elseif ($nY != $nX) {
|
} elseif ($yValueCount !== $xValueCount) {
|
||||||
// Ensure both arrays of points are the same size
|
// Ensure both arrays of points are the same size
|
||||||
$this->error = true;
|
$this->error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->valueCount = $nY;
|
$this->valueCount = $yValueCount;
|
||||||
$this->xValues = $xValues;
|
$this->xValues = $xValues;
|
||||||
$this->yValues = $yValues;
|
$this->yValues = $yValues;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,20 +88,17 @@ class ExponentialBestFit extends BestFit
|
||||||
*
|
*
|
||||||
* @param float[] $yValues The set of Y-values 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 float[] $xValues The set of X-values for this regression
|
||||||
* @param bool $const
|
|
||||||
*/
|
*/
|
||||||
private function exponentialRegression($yValues, $xValues, $const): void
|
private function exponentialRegression(array $yValues, array $xValues, bool $const): void
|
||||||
{
|
{
|
||||||
foreach ($yValues as &$value) {
|
$adjustedYValues = array_map(
|
||||||
if ($value < 0.0) {
|
function ($value) {
|
||||||
$value = 0 - log(abs($value));
|
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
|
||||||
} elseif ($value > 0.0) {
|
},
|
||||||
$value = log($value);
|
$yValues
|
||||||
}
|
);
|
||||||
}
|
|
||||||
unset($value);
|
|
||||||
|
|
||||||
$this->leastSquareFit($yValues, $xValues, $const);
|
$this->leastSquareFit($adjustedYValues, $xValues, $const);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -116,7 +113,7 @@ class ExponentialBestFit extends BestFit
|
||||||
parent::__construct($yValues, $xValues);
|
parent::__construct($yValues, $xValues);
|
||||||
|
|
||||||
if (!$this->error) {
|
if (!$this->error) {
|
||||||
$this->exponentialRegression($yValues, $xValues, $const);
|
$this->exponentialRegression($yValues, $xValues, (bool) $const);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,8 @@ class LinearBestFit extends BestFit
|
||||||
*
|
*
|
||||||
* @param float[] $yValues The set of Y-values 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 float[] $xValues The set of X-values for this regression
|
||||||
* @param bool $const
|
|
||||||
*/
|
*/
|
||||||
private function linearRegression($yValues, $xValues, $const): void
|
private function linearRegression(array $yValues, array $xValues, bool $const): void
|
||||||
{
|
{
|
||||||
$this->leastSquareFit($yValues, $xValues, $const);
|
$this->leastSquareFit($yValues, $xValues, $const);
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +74,7 @@ class LinearBestFit extends BestFit
|
||||||
parent::__construct($yValues, $xValues);
|
parent::__construct($yValues, $xValues);
|
||||||
|
|
||||||
if (!$this->error) {
|
if (!$this->error) {
|
||||||
$this->linearRegression($yValues, $xValues, $const);
|
$this->linearRegression($yValues, $xValues, (bool) $const);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class LogarithmicBestFit extends BestFit
|
||||||
$slope = $this->getSlope($dp);
|
$slope = $this->getSlope($dp);
|
||||||
$intersect = $this->getIntersect($dp);
|
$intersect = $this->getIntersect($dp);
|
||||||
|
|
||||||
return 'Y = ' . $intersect . ' + ' . $slope . ' * log(X)';
|
return 'Y = ' . $slope . ' * log(' . $intersect . ' * X)';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -56,20 +56,17 @@ class LogarithmicBestFit extends BestFit
|
||||||
*
|
*
|
||||||
* @param float[] $yValues The set of Y-values 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 float[] $xValues The set of X-values for this regression
|
||||||
* @param bool $const
|
|
||||||
*/
|
*/
|
||||||
private function logarithmicRegression($yValues, $xValues, $const): void
|
private function logarithmicRegression(array $yValues, array $xValues, bool $const): void
|
||||||
{
|
{
|
||||||
foreach ($xValues as &$value) {
|
$adjustedYValues = array_map(
|
||||||
if ($value < 0.0) {
|
function ($value) {
|
||||||
$value = 0 - log(abs($value));
|
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
|
||||||
} elseif ($value > 0.0) {
|
},
|
||||||
$value = log($value);
|
$yValues
|
||||||
}
|
);
|
||||||
}
|
|
||||||
unset($value);
|
|
||||||
|
|
||||||
$this->leastSquareFit($yValues, $xValues, $const);
|
$this->leastSquareFit($adjustedYValues, $xValues, $const);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,7 +81,7 @@ class LogarithmicBestFit extends BestFit
|
||||||
parent::__construct($yValues, $xValues);
|
parent::__construct($yValues, $xValues);
|
||||||
|
|
||||||
if (!$this->error) {
|
if (!$this->error) {
|
||||||
$this->logarithmicRegression($yValues, $xValues, $const);
|
$this->logarithmicRegression($yValues, $xValues, (bool) $const);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,9 +178,8 @@ class PolynomialBestFit extends BestFit
|
||||||
* @param int $order Order of Polynomial for this regression
|
* @param int $order Order of Polynomial for this regression
|
||||||
* @param float[] $yValues The set of Y-values 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 float[] $xValues The set of X-values for this regression
|
||||||
* @param bool $const
|
|
||||||
*/
|
*/
|
||||||
public function __construct($order, $yValues, $xValues = [], $const = true)
|
public function __construct($order, $yValues, $xValues = [])
|
||||||
{
|
{
|
||||||
parent::__construct($yValues, $xValues);
|
parent::__construct($yValues, $xValues);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,28 +72,23 @@ class PowerBestFit extends BestFit
|
||||||
*
|
*
|
||||||
* @param float[] $yValues The set of Y-values 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 float[] $xValues The set of X-values for this regression
|
||||||
* @param bool $const
|
|
||||||
*/
|
*/
|
||||||
private function powerRegression($yValues, $xValues, $const): void
|
private function powerRegression(array $yValues, array $xValues, bool $const): void
|
||||||
{
|
{
|
||||||
foreach ($xValues as &$value) {
|
$adjustedYValues = array_map(
|
||||||
if ($value < 0.0) {
|
function ($value) {
|
||||||
$value = 0 - log(abs($value));
|
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
|
||||||
} elseif ($value > 0.0) {
|
},
|
||||||
$value = log($value);
|
$yValues
|
||||||
}
|
);
|
||||||
}
|
$adjustedXValues = array_map(
|
||||||
unset($value);
|
function ($value) {
|
||||||
foreach ($yValues as &$value) {
|
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
|
||||||
if ($value < 0.0) {
|
},
|
||||||
$value = 0 - log(abs($value));
|
$xValues
|
||||||
} elseif ($value > 0.0) {
|
);
|
||||||
$value = log($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset($value);
|
|
||||||
|
|
||||||
$this->leastSquareFit($yValues, $xValues, $const);
|
$this->leastSquareFit($adjustedYValues, $adjustedXValues, $const);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -108,7 +103,7 @@ class PowerBestFit extends BestFit
|
||||||
parent::__construct($yValues, $xValues);
|
parent::__construct($yValues, $xValues);
|
||||||
|
|
||||||
if (!$this->error) {
|
if (!$this->error) {
|
||||||
$this->powerRegression($yValues, $xValues, $const);
|
$this->powerRegression($yValues, $xValues, (bool) $const);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,9 @@ class Trend
|
||||||
$nX = count($xValues);
|
$nX = count($xValues);
|
||||||
|
|
||||||
// Define X Values if necessary
|
// Define X Values if necessary
|
||||||
if ($nX == 0) {
|
if ($nX === 0) {
|
||||||
$xValues = range(1, $nY);
|
$xValues = range(1, $nY);
|
||||||
$nX = $nY;
|
} elseif ($nY !== $nX) {
|
||||||
} elseif ($nY != $nX) {
|
|
||||||
// Ensure both arrays of points are the same size
|
// Ensure both arrays of points are the same size
|
||||||
trigger_error('Trend(): Number of elements in coordinate arrays do not match.', E_USER_ERROR);
|
trigger_error('Trend(): Number of elements in coordinate arrays do not match.', E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +83,7 @@ class Trend
|
||||||
case self::TREND_POLYNOMIAL_6:
|
case self::TREND_POLYNOMIAL_6:
|
||||||
if (!isset(self::$trendCache[$key])) {
|
if (!isset(self::$trendCache[$key])) {
|
||||||
$order = substr($trendType, -1);
|
$order = substr($trendType, -1);
|
||||||
self::$trendCache[$key] = new PolynomialBestFit($order, $yValues, $xValues, $const);
|
self::$trendCache[$key] = new PolynomialBestFit($order, $yValues, $xValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$trendCache[$key];
|
return self::$trendCache[$key];
|
||||||
|
|
@ -100,7 +99,7 @@ class Trend
|
||||||
if ($trendType != self::TREND_BEST_FIT_NO_POLY) {
|
if ($trendType != self::TREND_BEST_FIT_NO_POLY) {
|
||||||
foreach (self::$trendTypePolynomialOrders as $trendMethod) {
|
foreach (self::$trendTypePolynomialOrders as $trendMethod) {
|
||||||
$order = substr($trendMethod, -1);
|
$order = substr($trendMethod, -1);
|
||||||
$bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues, $const);
|
$bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues);
|
||||||
if ($bestFit[$trendMethod]->getError()) {
|
if ($bestFit[$trendMethod]->getError()) {
|
||||||
unset($bestFit[$trendMethod]);
|
unset($bestFit[$trendMethod]);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class LogEstTest extends TestCase
|
||||||
public function testLOGEST($expectedResult, $yValues, $xValues, $const, $stats): void
|
public function testLOGEST($expectedResult, $yValues, $xValues, $const, $stats): void
|
||||||
{
|
{
|
||||||
$result = Statistical::LOGEST($yValues, $xValues, $const, $stats);
|
$result = Statistical::LOGEST($yValues, $xValues, $const, $stats);
|
||||||
|
//var_dump($result);
|
||||||
$elements = count($expectedResult);
|
$elements = count($expectedResult);
|
||||||
for ($element = 0; $element < $elements; ++$element) {
|
for ($element = 0; $element < $elements; ++$element) {
|
||||||
self::assertEqualsWithDelta($expectedResult[$element], $result[$element], 1E-12);
|
self::assertEqualsWithDelta($expectedResult[$element], $result[$element], 1E-12);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Shared\Trend;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Shared\Trend\ExponentialBestFit;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ExponentialBestFitTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider providerExponentialBestFit
|
||||||
|
*
|
||||||
|
* @param mixed $expectedSlope
|
||||||
|
* @param mixed $expectedIntersect
|
||||||
|
* @param mixed $expectedGoodnessOfFit
|
||||||
|
* @param mixed $yValues
|
||||||
|
* @param mixed $xValues
|
||||||
|
* @param mixed $expectedEquation
|
||||||
|
*/
|
||||||
|
public function testExponentialBestFit(
|
||||||
|
$expectedSlope,
|
||||||
|
$expectedIntersect,
|
||||||
|
$expectedGoodnessOfFit,
|
||||||
|
$expectedEquation,
|
||||||
|
$yValues,
|
||||||
|
$xValues
|
||||||
|
): void {
|
||||||
|
$bestFit = new ExponentialBestFit($yValues, $xValues);
|
||||||
|
$slope = $bestFit->getSlope(1);
|
||||||
|
self::assertEquals($expectedSlope[0], $slope);
|
||||||
|
$slope = $bestFit->getSlope();
|
||||||
|
self::assertEquals($expectedSlope[1], $slope);
|
||||||
|
$intersect = $bestFit->getIntersect(1);
|
||||||
|
self::assertEquals($expectedIntersect[0], $intersect);
|
||||||
|
$intersect = $bestFit->getIntersect();
|
||||||
|
self::assertEquals($expectedIntersect[1], $intersect);
|
||||||
|
|
||||||
|
$equation = $bestFit->getEquation(2);
|
||||||
|
self::assertEquals($expectedEquation, $equation);
|
||||||
|
|
||||||
|
self::assertSame($expectedGoodnessOfFit[0], $bestFit->getGoodnessOfFit(6));
|
||||||
|
self::assertSame($expectedGoodnessOfFit[1], $bestFit->getGoodnessOfFit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerExponentialBestFit()
|
||||||
|
{
|
||||||
|
return require 'tests/data/Shared/Trend/ExponentialBestFit.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Shared\Trend;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Shared\Trend\LinearBestFit;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class LinearBestFitTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider providerLinearBestFit
|
||||||
|
*
|
||||||
|
* @param mixed $expectedSlope
|
||||||
|
* @param mixed $expectedIntersect
|
||||||
|
* @param mixed $expectedGoodnessOfFit
|
||||||
|
* @param mixed $yValues
|
||||||
|
* @param mixed $xValues
|
||||||
|
* @param mixed $expectedEquation
|
||||||
|
*/
|
||||||
|
public function testLinearBestFit(
|
||||||
|
$expectedSlope,
|
||||||
|
$expectedIntersect,
|
||||||
|
$expectedGoodnessOfFit,
|
||||||
|
$expectedEquation,
|
||||||
|
$yValues,
|
||||||
|
$xValues
|
||||||
|
): void {
|
||||||
|
$bestFit = new LinearBestFit($yValues, $xValues);
|
||||||
|
$slope = $bestFit->getSlope(1);
|
||||||
|
self::assertEquals($expectedSlope[0], $slope);
|
||||||
|
$slope = $bestFit->getSlope();
|
||||||
|
self::assertEquals($expectedSlope[1], $slope);
|
||||||
|
$intersect = $bestFit->getIntersect(1);
|
||||||
|
self::assertEquals($expectedIntersect[0], $intersect);
|
||||||
|
$intersect = $bestFit->getIntersect();
|
||||||
|
self::assertEquals($expectedIntersect[1], $intersect);
|
||||||
|
|
||||||
|
$equation = $bestFit->getEquation(2);
|
||||||
|
self::assertEquals($expectedEquation, $equation);
|
||||||
|
|
||||||
|
self::assertSame($expectedGoodnessOfFit[0], $bestFit->getGoodnessOfFit(6));
|
||||||
|
self::assertSame($expectedGoodnessOfFit[1], $bestFit->getGoodnessOfFit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerLinearBestFit()
|
||||||
|
{
|
||||||
|
return require 'tests/data/Shared/Trend/LinearBestFit.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,22 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
[
|
||||||
|
[1.0, 0.0],
|
||||||
|
[1, 2, 3, 4, 5],
|
||||||
|
[1, 2, 3, 4, 5],
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[2.310344827586207, 0.0],
|
||||||
|
[1, 9, 5, 7],
|
||||||
|
[0, 4, 2, 3],
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
[2.0, 1.0],
|
[2.0, 1.0],
|
||||||
[1, 9, 5, 7],
|
[1, 9, 5, 7],
|
||||||
|
|
@ -8,6 +24,46 @@ return [
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
[0.600378787879, 0.0],
|
||||||
|
[3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
[8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[-1.1064189189190, 14.081081081081],
|
||||||
|
[3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
[8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[0.600378787879, 0.0],
|
||||||
|
[0.3130441135917, Functions::NA()],
|
||||||
|
[0.2901220667036, 7.193206086629],
|
||||||
|
[3.6782360429317, 9],
|
||||||
|
[190.3200757575760, 465.679924242424],
|
||||||
|
],
|
||||||
|
[3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
[8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[-1.1064189189190, 14.081081081081],
|
||||||
|
[0.1491074289251, 1.083468383961],
|
||||||
|
[0.8731378215565, 1.622464237727],
|
||||||
|
[55.0605598780781, 8],
|
||||||
|
[144.9408783783780, 21.059121621622],
|
||||||
|
],
|
||||||
|
[3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
[8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
[56.837944664032, 11704.347826086974],
|
[56.837944664032, 11704.347826086974],
|
||||||
|
|
@ -21,29 +77,29 @@ return [
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
],
|
],
|
||||||
// [
|
// 'multi-series' => [
|
||||||
// [
|
// [
|
||||||
// [-234.2371645, 2553.21066, 12529.76817, 27.64138737, 52317.83051],
|
// [-234.2371645, 2553.21066, 12529.76817, 27.64138737, 52317.83051],
|
||||||
// [13.26801148, 530.6691519, 400.0668382, 5.429374042, 12237.3616],
|
// [13.26801148, 530.6691519, 400.0668382, 5.429374042, 12237.3616],
|
||||||
// [0.996747993, 970.5784629, '#N/A', '#N/A', '#N/A'],
|
// [0.996747993, 970.5784629, '#N/A', '#N/A', '#N/A'],
|
||||||
// [459.7536742, 6, '#N/A', '#N/A', '#N/A'],
|
// [459.7536742, 6, '#N/A', '#N/A', '#N/A'],
|
||||||
// [1732393319, 5652135.316, '#N/A', '#N/A', '#N/A'],
|
// [1732393319, 5652135.316, '#N/A', '#N/A', '#N/A'],
|
||||||
|
// ],
|
||||||
|
// [142000, 144000, 151000, 150000, 139000, 169000, 126000, 142900, 163000, 169000, 149000],
|
||||||
|
// [
|
||||||
|
// [2310, 2, 2, 20],
|
||||||
|
// [2333, 2, 2, 12],
|
||||||
|
// [2356, 3, 1.5, 33],
|
||||||
|
// [2379, 3, 2, 43],
|
||||||
|
// [2402, 2, 3, 53],
|
||||||
|
// [2425, 4, 2, 23],
|
||||||
|
// [2448, 2, 1.5, 99],
|
||||||
|
// [2471, 2, 2, 34],
|
||||||
|
// [2494, 3, 3, 23],
|
||||||
|
// [2517, 4, 4, 55],
|
||||||
|
// [2540, 2, 3, 22],
|
||||||
|
// ],
|
||||||
|
// true,
|
||||||
|
// true,
|
||||||
// ],
|
// ],
|
||||||
// [142000, 144000, 151000, 150000, 139000, 169000, 126000, 142900, 163000, 169000, 149000],
|
|
||||||
// [
|
|
||||||
// [2310, 2, 2, 20],
|
|
||||||
// [2333, 2, 2, 12],
|
|
||||||
// [2356, 3, 1.5, 33],
|
|
||||||
// [2379, 3, 2, 43],
|
|
||||||
// [2402, 2, 3, 53],
|
|
||||||
// [2425, 4, 2, 23],
|
|
||||||
// [2448, 2, 1.5, 99],
|
|
||||||
// [2471, 2, 2, 34],
|
|
||||||
// [2494, 3, 3, 23],
|
|
||||||
// [2517, 4, 4, 55],
|
|
||||||
// [2540, 2, 3, 22],
|
|
||||||
// ],
|
|
||||||
// true,
|
|
||||||
// true,
|
|
||||||
// ],
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
[
|
||||||
|
[1.000174230092, 1.0],
|
||||||
|
[1, 2, 3, 4, 5],
|
||||||
|
[1, 10, 100, 1000, 10000],
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[1.000091183030, 2.127357620703],
|
||||||
|
[1, 2, 3, 4, 5],
|
||||||
|
[1, 10, 100, 1000, 10000],
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
[1.463275628116, 495.304770158727],
|
[1.463275628116, 495.304770158727],
|
||||||
[33100, 47300, 69000, 102000, 150000, 220000],
|
[33100, 47300, 69000, 102000, 150000, 220000],
|
||||||
|
|
@ -15,4 +29,44 @@ return [
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
[1.1743674215053, 1.0],
|
||||||
|
[3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
[8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[0.8135120728565, 20.671878197178],
|
||||||
|
[3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
[8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
// [
|
||||||
|
// [
|
||||||
|
// [1.174367421505266, 1.0],
|
||||||
|
// [0.0672620306083, Functions::NA()],
|
||||||
|
// [0.3881799938732, 1.545563794251],
|
||||||
|
// [5.7102087376569, 9],
|
||||||
|
// [13.6403607201119, 21.498906978904],
|
||||||
|
// ],
|
||||||
|
// [3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
// [8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
// false,
|
||||||
|
// true,
|
||||||
|
// ],
|
||||||
|
// [
|
||||||
|
// [
|
||||||
|
// [0.8135120728565, 20.671878197178],
|
||||||
|
// [0.0313021171611, 0.227452478657],
|
||||||
|
// [0.8445875527654, 0.340603858743],
|
||||||
|
// [43.4759283593386, 8],
|
||||||
|
// [5.0436854288511, 0.928087908723],
|
||||||
|
// ],
|
||||||
|
// [3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
// [8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
// true,
|
||||||
|
// true,
|
||||||
|
// ],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'slope' => [0.8, 0.813512072856517],
|
||||||
|
'intersect' => [20.7, 20.671878197177865],
|
||||||
|
'goodnessOfFit' => [0.904868, 0.9048681877346413],
|
||||||
|
'equation' => 'Y = 20.67 * 0.81^X',
|
||||||
|
[3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
[8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'slope' => [-1.1, -1.1064189189190],
|
||||||
|
'intersect' => [14.1, 14.081081081081],
|
||||||
|
'goodnessOfFit' => [0.873138, 0.8731378215564962],
|
||||||
|
'equation' => 'Y = 14.08 + -1.11 * X',
|
||||||
|
[3, 10, 3, 6, 8, 12, 1, 4, 9, 14],
|
||||||
|
[8, 2, 11, 6, 5, 4, 12, 9, 6, 1],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'slope' => [1.0, 1.0],
|
||||||
|
'intersect' => [-2.0, -2.0],
|
||||||
|
'goodnessOfFit' => [1.0, 1.0],
|
||||||
|
'equation' => 'Y = -2 + 1 * X',
|
||||||
|
[1, 2, 3, 4, 5],
|
||||||
|
[3, 4, 5, 6, 7],
|
||||||
|
],
|
||||||
|
];
|
||||||
Loading…
Reference in New Issue