From 459d27c7c81fe39f8f59d44734a5593e0b21c032 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Thu, 4 Mar 2021 20:39:51 +0100 Subject: [PATCH 01/18] - Move TREND() functions into the Statistical Trends class - Unit tests forTREND() - Create Confidence class for Statistical Confidence functions --- .../Calculation/Calculation.php | 6 +-- .../Calculation/Statistical.php | 22 +++------- .../Calculation/Statistical/Confidence.php | 41 +++++++++++++++++++ .../Calculation/Statistical/Trends.php | 32 +++++++++++++++ .../Functions/Statistical/TrendTest.php | 32 +++++++++++++++ tests/data/Calculation/Statistical/TREND.php | 16 ++++++++ 6 files changed, 130 insertions(+), 19 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Confidence.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php create mode 100644 tests/data/Calculation/Statistical/TREND.php diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 04b8ffab9f..0d06f04eaa 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -579,12 +579,12 @@ class Calculation ], 'CONFIDENCE' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'CONFIDENCE'], + 'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'], 'argumentCount' => '3', ], 'CONFIDENCE.NORM' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'CONFIDENCE'], + 'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'], 'argumentCount' => '3', ], 'CONFIDENCE.T' => [ @@ -2454,7 +2454,7 @@ class Calculation ], 'TREND' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'TREND'], + 'functionCall' => [Statistical\Trends::class, 'TREND'], 'argumentCount' => '1-4', ], 'TRIM' => [ diff --git a/src/PhpSpreadsheet/Calculation/Statistical.php b/src/PhpSpreadsheet/Calculation/Statistical.php index 4b5c8094d0..6ae8564d87 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical.php +++ b/src/PhpSpreadsheet/Calculation/Statistical.php @@ -2941,6 +2941,11 @@ public static function TINV($probability, $degrees) * * Returns values along a linear Trend * + * @Deprecated 1.18.0 + * + * @see Statistical\Trends::TREND() + * Use the TREND() method in the Statistical\Trends class instead + * * @param mixed[] $yValues Data Series Y * @param mixed[] $xValues Data Series X * @param mixed[] $newValues Values of X for which we want to find Y @@ -2950,22 +2955,7 @@ public static function TINV($probability, $degrees) */ public static function TREND($yValues, $xValues = [], $newValues = [], $const = true) { - $yValues = Functions::flattenArray($yValues); - $xValues = Functions::flattenArray($xValues); - $newValues = Functions::flattenArray($newValues); - $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const); - - $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const); - if (empty($newValues)) { - $newValues = $bestFitLinear->getXValues(); - } - - $returnArray = []; - foreach ($newValues as $xValue) { - $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue); - } - - return $returnArray; + return Trends::TREND($yValues, $xValues, $newValues, $const); } /** diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php b/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php new file mode 100644 index 0000000000..c4c2a7dd91 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php @@ -0,0 +1,41 @@ += 1)) { + return Functions::NAN(); + } + if (($stdDev <= 0) || ($size < 1)) { + return Functions::NAN(); + } + + return Statistical::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size); + } + + return Functions::VALUE(); + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Trends.php b/src/PhpSpreadsheet/Calculation/Statistical/Trends.php index 23bffced43..d06214de2f 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Trends.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Trends.php @@ -388,4 +388,36 @@ public static function STEYX($yValues, $xValues) return $bestFitLinear->getStdevOfResiduals(); } + + /** + * TREND. + * + * Returns values along a linear Trend + * + * @param mixed[] $yValues Data Series Y + * @param mixed[] $xValues Data Series X + * @param mixed[] $newValues Values of X for which we want to find Y + * @param bool $const a logical value specifying whether to force the intersect to equal 0 + * + * @return array of float + */ + public static function TREND($yValues, $xValues = [], $newValues = [], $const = true) + { + $yValues = Functions::flattenArray($yValues); + $xValues = Functions::flattenArray($xValues); + $newValues = Functions::flattenArray($newValues); + $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const); + + $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const); + if (empty($newValues)) { + $newValues = $bestFitLinear->getXValues(); + } + + $returnArray = []; + foreach ($newValues as $xValue) { + $returnArray[0][] = [$bestFitLinear->getValueOfYForX($xValue)]; + } + + return $returnArray; + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php new file mode 100644 index 0000000000..d3388cdb39 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php @@ -0,0 +1,32 @@ + Date: Thu, 4 Mar 2021 20:41:16 +0100 Subject: [PATCH 02/18] PHPCS --- tests/data/Calculation/Statistical/TREND.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/data/Calculation/Statistical/TREND.php b/tests/data/Calculation/Statistical/TREND.php index e9f0106cf8..a830954613 100644 --- a/tests/data/Calculation/Statistical/TREND.php +++ b/tests/data/Calculation/Statistical/TREND.php @@ -7,10 +7,10 @@ [147189.69696969696], [148207.87878787878], [149226.0606060606], - [150244.24242424243] + [150244.24242424243], ], [133890, 135000, 135790, 137300, 138130, 139100, 139900, 141120, 141890, 143230, 144000, 145290], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17], ], -]; \ No newline at end of file +]; From c1634ce05a7d8fede471ac7c0fd3c34865a9c84d Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Thu, 4 Mar 2021 20:59:00 +0100 Subject: [PATCH 03/18] Keep Scrutinizer happy --- .../Calculation/Functions/Statistical/GrowthTest.php | 5 +++-- .../Calculation/Functions/Statistical/TrendTest.php | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/GrowthTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/GrowthTest.php index ce4eef8a23..6e1cf7bfa4 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/GrowthTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/GrowthTest.php @@ -17,10 +17,11 @@ protected function setUp(): void * @dataProvider providerGROWTH * * @param mixed $expectedResult + * @param mixed $yValues */ - public function testGROWTH($expectedResult, ...$args): void + public function testGROWTH($expectedResult, $yValues, ...$args): void { - $result = Statistical::GROWTH(...$args); + $result = Statistical::GROWTH($yValues, ...$args); self::assertEqualsWithDelta($expectedResult, $result[0], 1E-12); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php index d3388cdb39..b4f756b579 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/TrendTest.php @@ -17,10 +17,11 @@ protected function setUp(): void * @dataProvider providerGROWTH * * @param mixed $expectedResult + * @param mixed $yValues */ - public function testTREND($expectedResult, ...$args): void + public function testTREND($expectedResult, $yValues, ...$args): void { - $result = Statistical::TREND(...$args); + $result = Statistical::TREND($yValues, ...$args); self::assertEqualsWithDelta($expectedResult, $result[0], 1E-12); } From 1e1a8b5b384b07a53a725a56ad7d1343cdff6688 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Thu, 4 Mar 2021 21:07:27 +0100 Subject: [PATCH 04/18] Make Scrutinizer even happier --- tests/PhpSpreadsheetTests/Shared/FontTest.php | 15 +++++++++------ tests/PhpSpreadsheetTests/Style/ColorTest.php | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/PhpSpreadsheetTests/Shared/FontTest.php b/tests/PhpSpreadsheetTests/Shared/FontTest.php index 94ab74b632..bc917654ca 100644 --- a/tests/PhpSpreadsheetTests/Shared/FontTest.php +++ b/tests/PhpSpreadsheetTests/Shared/FontTest.php @@ -41,10 +41,11 @@ public function testSetAutoSizeMethodWithInvalidValue(): void * @dataProvider providerFontSizeToPixels * * @param mixed $expectedResult + * @param mixed $size */ - public function testFontSizeToPixels($expectedResult, ...$args): void + public function testFontSizeToPixels($expectedResult, $size): void { - $result = Font::fontSizeToPixels(...$args); + $result = Font::fontSizeToPixels($size); self::assertEquals($expectedResult, $result); } @@ -57,10 +58,11 @@ public function providerFontSizeToPixels() * @dataProvider providerInchSizeToPixels * * @param mixed $expectedResult + * @param mixed $size */ - public function testInchSizeToPixels($expectedResult, ...$args): void + public function testInchSizeToPixels($expectedResult, $size): void { - $result = Font::inchSizeToPixels(...$args); + $result = Font::inchSizeToPixels($size); self::assertEquals($expectedResult, $result); } @@ -73,10 +75,11 @@ public function providerInchSizeToPixels() * @dataProvider providerCentimeterSizeToPixels * * @param mixed $expectedResult + * @param mixed $size */ - public function testCentimeterSizeToPixels($expectedResult, ...$args): void + public function testCentimeterSizeToPixels($expectedResult, $size): void { - $result = Font::centimeterSizeToPixels(...$args); + $result = Font::centimeterSizeToPixels($size); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Style/ColorTest.php b/tests/PhpSpreadsheetTests/Style/ColorTest.php index 04028f871f..2c017ff4eb 100644 --- a/tests/PhpSpreadsheetTests/Style/ColorTest.php +++ b/tests/PhpSpreadsheetTests/Style/ColorTest.php @@ -11,10 +11,11 @@ class ColorTest extends TestCase * @dataProvider providerColorGetRed * * @param mixed $expectedResult + * @param mixed $color */ - public function testGetRed($expectedResult, ...$args): void + public function testGetRed($expectedResult, $color, ...$args): void { - $result = Color::getRed(...$args); + $result = Color::getRed($color, ...$args); self::assertEquals($expectedResult, $result); } @@ -27,10 +28,11 @@ public function providerColorGetRed() * @dataProvider providerColorGetGreen * * @param mixed $expectedResult + * @param mixed $color */ - public function testGetGreen($expectedResult, ...$args): void + public function testGetGreen($expectedResult, $color, ...$args): void { - $result = Color::getGreen(...$args); + $result = Color::getGreen($color, ...$args); self::assertEquals($expectedResult, $result); } @@ -43,10 +45,11 @@ public function providerColorGetGreen() * @dataProvider providerColorGetBlue * * @param mixed $expectedResult + * @param mixed $color */ - public function testGetBlue($expectedResult, ...$args): void + public function testGetBlue($expectedResult, $color, ...$args): void { - $result = Color::getBlue(...$args); + $result = Color::getBlue($color, ...$args); self::assertEquals($expectedResult, $result); } From 877d14be5b59321cb7012731df9643ca8283f64f Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Thu, 4 Mar 2021 21:25:46 +0100 Subject: [PATCH 05/18] Deprecate the old CONFIDENCE() method --- .../Calculation/Statistical.php | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/Statistical.php b/src/PhpSpreadsheet/Calculation/Statistical.php index 6ae8564d87..8a9e3fea33 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical.php +++ b/src/PhpSpreadsheet/Calculation/Statistical.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Conditional; +use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Confidence; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Counts; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Maximum; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Minimum; @@ -832,6 +833,11 @@ public static function CHIINV($probability, $degrees) * * Returns the confidence interval for a population mean * + * @Deprecated 1.18.0 + * + * @see Statistical\Confidence::CONFIDENCE() + * Use the CONFIDENCE() method in the Statistical\Confidence class instead + * * @param float $alpha * @param float $stdDev Standard Deviation * @param float $size @@ -840,23 +846,7 @@ public static function CHIINV($probability, $degrees) */ public static function CONFIDENCE($alpha, $stdDev, $size) { - $alpha = Functions::flattenSingleValue($alpha); - $stdDev = Functions::flattenSingleValue($stdDev); - $size = Functions::flattenSingleValue($size); - - if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) { - $size = floor($size); - if (($alpha <= 0) || ($alpha >= 1)) { - return Functions::NAN(); - } - if (($stdDev <= 0) || ($size < 1)) { - return Functions::NAN(); - } - - return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size); - } - - return Functions::VALUE(); + return Confidence::CONFIDENCE($alpha, $stdDev, $size); } /** From d3ba7fd5b1fad2ceb7052a20d3d63a90528116e1 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Thu, 4 Mar 2021 21:32:19 +0100 Subject: [PATCH 06/18] Additional test case for TREND() --- tests/data/Calculation/Statistical/TREND.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/data/Calculation/Statistical/TREND.php b/tests/data/Calculation/Statistical/TREND.php index a830954613..418367b2e5 100644 --- a/tests/data/Calculation/Statistical/TREND.php +++ b/tests/data/Calculation/Statistical/TREND.php @@ -1,6 +1,24 @@ Date: Thu, 4 Mar 2021 23:45:56 +0100 Subject: [PATCH 07/18] Unit tests from Shared Trend classes --- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 2 +- .../Shared/Trend/LinearBestFitTest.php | 43 +++++++++++++++++++ tests/data/Shared/Trend/LinearBestFit.php | 11 +++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php create mode 100644 tests/data/Shared/Trend/LinearBestFit.php diff --git a/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/src/PhpSpreadsheet/Shared/Trend/BestFit.php index c94997226b..69fa81dabd 100644 --- a/src/PhpSpreadsheet/Shared/Trend/BestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -451,7 +451,7 @@ public function __construct($yValues, $xValues = [], $const = true) // Define X Values if necessary if ($nX == 0) { $xValues = range(1, $nY); - } elseif ($nY != $nX) { + } elseif ($nY !== $nX) { // Ensure both arrays of points are the same size $this->error = true; } diff --git a/tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php b/tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php new file mode 100644 index 0000000000..12f63c5b73 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php @@ -0,0 +1,43 @@ +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); + self::assertSame($expectedGoodnessOfFit[0], $bestFit->getGoodnessOfFit(6)); + self::assertSame($expectedGoodnessOfFit[1], $bestFit->getGoodnessOfFit()); + } + + public function providerLinearBestFit() + { + return require 'tests/data/Shared/Trend/LinearBestFit.php'; + } +} diff --git a/tests/data/Shared/Trend/LinearBestFit.php b/tests/data/Shared/Trend/LinearBestFit.php new file mode 100644 index 0000000000..43c68151c5 --- /dev/null +++ b/tests/data/Shared/Trend/LinearBestFit.php @@ -0,0 +1,11 @@ + [-1.1, -1.1064189189190], + 'intersect' => [14.1, 14.081081081081], + 'goodnessOfFit' => [0.873138, 0.8731378215564962], + [3, 10, 3, 6, 8, 12, 1, 4, 9, 14], + [8, 2, 11, 6, 5, 4, 12, 9, 6, 1], + ], +]; From 9568a3745aea381c03b4d2e9326d7bc20d9e3be6 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Fri, 5 Mar 2021 16:37:53 +0100 Subject: [PATCH 08/18] Unit tests from Shared Trend classes --- .../Calculation/Statistical/Trends.php | 4 +- .../Shared/Trend/ExponentialBestFit.php | 16 ++- .../Shared/Trend/LogarithmicBestFit.php | 18 ++-- .../Shared/Trend/PowerBestFit.php | 30 +++--- .../Functions/Statistical/LogEstTest.php | 2 +- .../Shared/Trend/ExponentialBestFitTest.php | 49 ++++++++++ .../Shared/Trend/LinearBestFitTest.php | 8 +- tests/data/Calculation/Statistical/LINEST.php | 97 ++++++++++++++----- tests/data/Calculation/Statistical/LOGEST.php | 42 ++++++++ .../data/Shared/Trend/ExponentialBestFit.php | 12 +++ tests/data/Shared/Trend/LinearBestFit.php | 1 + 11 files changed, 215 insertions(+), 64 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Shared/Trend/ExponentialBestFitTest.php create mode 100644 tests/data/Shared/Trend/ExponentialBestFit.php diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Trends.php b/src/PhpSpreadsheet/Calculation/Statistical/Trends.php index d06214de2f..a1137cefc1 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Trends.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Trends.php @@ -226,7 +226,7 @@ public static function LINEST($yValues, $xValues = null, $const = true, $stats = ], [ $bestFitLinear->getSlopeSE(), - $bestFitLinear->getIntersectSE(), + ($const === false) ? Functions::NA() : $bestFitLinear->getIntersectSE(), ], [ $bestFitLinear->getGoodnessOfFit(), @@ -293,7 +293,7 @@ public static function LOGEST($yValues, $xValues = null, $const = true, $stats = ], [ $bestFitExponential->getSlopeSE(), - $bestFitExponential->getIntersectSE(), + ($const === false) ? Functions::NA() : $bestFitExponential->getIntersectSE(), ], [ $bestFitExponential->getGoodnessOfFit(), diff --git a/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php b/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php index 82866dee80..f5feb803a4 100644 --- a/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php @@ -92,16 +92,14 @@ public function getIntersect($dp = 0) */ private function exponentialRegression($yValues, $xValues, $const): void { - foreach ($yValues as &$value) { - if ($value < 0.0) { - $value = 0 - log(abs($value)); - } elseif ($value > 0.0) { - $value = log($value); - } - } - unset($value); + $adjustedYValues = array_map( + function ($value) { + return ($value < 0.0) ? 0 - log(abs($value)) : log($value); + }, + $yValues + ); - $this->leastSquareFit($yValues, $xValues, $const); + $this->leastSquareFit($adjustedYValues, $xValues, $const); } /** diff --git a/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php b/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php index c469067d47..404151b5e9 100644 --- a/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php @@ -48,7 +48,7 @@ public function getEquation($dp = 0) $slope = $this->getSlope($dp); $intersect = $this->getIntersect($dp); - return 'Y = ' . $intersect . ' + ' . $slope . ' * log(X)'; + return 'Y = ' . $slope . ' * log(' . $intersect . ' * X)'; } /** @@ -60,16 +60,14 @@ public function getEquation($dp = 0) */ private function logarithmicRegression($yValues, $xValues, $const): void { - foreach ($xValues as &$value) { - if ($value < 0.0) { - $value = 0 - log(abs($value)); - } elseif ($value > 0.0) { - $value = log($value); - } - } - unset($value); + $adjustedYValues = array_map( + function ($value) { + return ($value < 0.0) ? 0 - log(abs($value)) : log($value); + }, + $yValues + ); - $this->leastSquareFit($yValues, $xValues, $const); + $this->leastSquareFit($adjustedYValues, $xValues, $const); } /** diff --git a/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php b/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php index c53eab6381..f05b826d64 100644 --- a/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php @@ -76,24 +76,20 @@ public function getIntersect($dp = 0) */ private function powerRegression($yValues, $xValues, $const): void { - 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); + $adjustedYValues = array_map( + function ($value) { + return ($value < 0.0) ? 0 - log(abs($value)) : log($value); + }, + $yValues + ); + $adjustedXValues = array_map( + function ($value) { + return ($value < 0.0) ? 0 - log(abs($value)) : log($value); + }, + $xValues + ); - $this->leastSquareFit($yValues, $xValues, $const); + $this->leastSquareFit($adjustedYValues, $adjustedXValues, $const); } /** diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LogEstTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LogEstTest.php index 4d926f7620..2b2d1ecf12 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LogEstTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/LogEstTest.php @@ -19,7 +19,7 @@ class LogEstTest extends TestCase public function testLOGEST($expectedResult, $yValues, $xValues, $const, $stats): void { $result = Statistical::LOGEST($yValues, $xValues, $const, $stats); - + //var_dump($result); $elements = count($expectedResult); for ($element = 0; $element < $elements; ++$element) { self::assertEqualsWithDelta($expectedResult[$element], $result[$element], 1E-12); diff --git a/tests/PhpSpreadsheetTests/Shared/Trend/ExponentialBestFitTest.php b/tests/PhpSpreadsheetTests/Shared/Trend/ExponentialBestFitTest.php new file mode 100644 index 0000000000..32fa9d3187 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Shared/Trend/ExponentialBestFitTest.php @@ -0,0 +1,49 @@ +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'; + } +} diff --git a/tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php b/tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php index 12f63c5b73..02b82038c0 100644 --- a/tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php +++ b/tests/PhpSpreadsheetTests/Shared/Trend/LinearBestFitTest.php @@ -15,11 +15,13 @@ class LinearBestFitTest extends TestCase * @param mixed $expectedGoodnessOfFit * @param mixed $yValues * @param mixed $xValues + * @param mixed $expectedEquation */ - public function testCodePageNumberToName( + public function testLinearBestFit( $expectedSlope, $expectedIntersect, $expectedGoodnessOfFit, + $expectedEquation, $yValues, $xValues ): void { @@ -32,6 +34,10 @@ public function testCodePageNumberToName( 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()); } diff --git a/tests/data/Calculation/Statistical/LINEST.php b/tests/data/Calculation/Statistical/LINEST.php index 9bd28ffe29..ff1913bc56 100644 --- a/tests/data/Calculation/Statistical/LINEST.php +++ b/tests/data/Calculation/Statistical/LINEST.php @@ -1,6 +1,15 @@ [ + // [ + // [-234.2371645, 2553.21066, 12529.76817, 27.64138737, 52317.83051], + // [13.26801148, 530.6691519, 400.0668382, 5.429374042, 12237.3616], + // [0.996747993, 970.5784629, '#N/A', '#N/A', '#N/A'], + // [459.7536742, 6, '#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, // ], - // true, - // true, - // ], ]; diff --git a/tests/data/Calculation/Statistical/LOGEST.php b/tests/data/Calculation/Statistical/LOGEST.php index be9e4d7252..38a2ff8d75 100644 --- a/tests/data/Calculation/Statistical/LOGEST.php +++ b/tests/data/Calculation/Statistical/LOGEST.php @@ -1,5 +1,7 @@ [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], + ], +]; diff --git a/tests/data/Shared/Trend/LinearBestFit.php b/tests/data/Shared/Trend/LinearBestFit.php index 43c68151c5..1997d404bd 100644 --- a/tests/data/Shared/Trend/LinearBestFit.php +++ b/tests/data/Shared/Trend/LinearBestFit.php @@ -5,6 +5,7 @@ '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], ], From 31c2968568d238575e355fbf68ed9d67342622f2 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Fri, 5 Mar 2021 18:29:21 +0100 Subject: [PATCH 09/18] Minor typing improvements --- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 30 ++++++++----------- .../Shared/Trend/ExponentialBestFit.php | 4 +-- .../Shared/Trend/LinearBestFit.php | 4 +-- .../Shared/Trend/LogarithmicBestFit.php | 4 +-- .../Shared/Trend/PolynomialBestFit.php | 3 +- .../Shared/Trend/PowerBestFit.php | 4 +-- src/PhpSpreadsheet/Shared/Trend/Trend.php | 9 +++--- 7 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/src/PhpSpreadsheet/Shared/Trend/BestFit.php index 69fa81dabd..3a1f0b1a1f 100644 --- a/src/PhpSpreadsheet/Shared/Trend/BestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -348,13 +348,13 @@ protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue); $SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY); - if ($const) { + if ($const === true) { $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) { + if ($const === true) { $SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX); } else { $SSsex += $this->xValues[$xKey] * $this->xValues[$xKey]; @@ -398,9 +398,8 @@ protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, /** * @param float[] $yValues * @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 $x_sum = array_sum($xValues); @@ -413,7 +412,7 @@ protected function leastSquareFit(array $yValues, array $xValues, $const): void $xx_sum += $xValues[$i] * $xValues[$i]; $yy_sum += $yValues[$i] * $yValues[$i]; - if ($const) { + if ($const === true) { $mBase += ($xValues[$i] - $meanX) * ($yValues[$i] - $meanY); $mDivisor += ($xValues[$i] - $meanX) * ($xValues[$i] - $meanX); } else { @@ -426,11 +425,7 @@ protected function leastSquareFit(array $yValues, array $xValues, $const): void $this->slope = $mBase / $mDivisor; // calculate intersect - if ($const) { - $this->intersect = $meanY - ($this->slope * $meanX); - } else { - $this->intersect = 0; - } + $this->intersect = ($const === true) ? $meanY - ($this->slope * $meanX) : 0.0; $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, $meanX, $meanY, $const); } @@ -440,23 +435,22 @@ protected function leastSquareFit(array $yValues, array $xValues, $const): void * * @param float[] $yValues The set of Y-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 - $nY = count($yValues); - $nX = count($xValues); + $yValueCount = count($yValues); + $xValueCount = count($xValues); // Define X Values if necessary - if ($nX == 0) { - $xValues = range(1, $nY); - } elseif ($nY !== $nX) { + if ($xValueCount === 0) { + $xValues = range(1, $yValueCount); + } elseif ($yValueCount !== $xValueCount) { // Ensure both arrays of points are the same size $this->error = true; } - $this->valueCount = $nY; + $this->valueCount = $yValueCount; $this->xValues = $xValues; $this->yValues = $yValues; } diff --git a/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php b/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php index f5feb803a4..570a12ab17 100644 --- a/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php @@ -90,7 +90,7 @@ public function getIntersect($dp = 0) * @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 { $adjustedYValues = array_map( function ($value) { @@ -114,7 +114,7 @@ public function __construct($yValues, $xValues = [], $const = true) parent::__construct($yValues, $xValues); if (!$this->error) { - $this->exponentialRegression($yValues, $xValues, $const); + $this->exponentialRegression($yValues, $xValues, (bool) $const); } } } diff --git a/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php b/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php index 26a562c551..2208447181 100644 --- a/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php @@ -58,7 +58,7 @@ public function getEquation($dp = 0) * @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); } @@ -75,7 +75,7 @@ public function __construct($yValues, $xValues = [], $const = true) parent::__construct($yValues, $xValues); if (!$this->error) { - $this->linearRegression($yValues, $xValues, $const); + $this->linearRegression($yValues, $xValues, (bool) $const); } } } diff --git a/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php b/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php index 404151b5e9..e273263447 100644 --- a/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php @@ -58,7 +58,7 @@ public function getEquation($dp = 0) * @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 { $adjustedYValues = array_map( function ($value) { @@ -82,7 +82,7 @@ public function __construct($yValues, $xValues = [], $const = true) parent::__construct($yValues, $xValues); if (!$this->error) { - $this->logarithmicRegression($yValues, $xValues, $const); + $this->logarithmicRegression($yValues, $xValues, (bool) $const); } } } diff --git a/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php b/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php index d959eddb75..1d34e81c19 100644 --- a/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php @@ -178,9 +178,8 @@ private function polynomialRegression($order, $yValues, $xValues): void * @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 bool $const */ - public function __construct($order, $yValues, $xValues = [], $const = true) + public function __construct($order, $yValues, $xValues = []) { parent::__construct($yValues, $xValues); diff --git a/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php b/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php index f05b826d64..46ee8c6a14 100644 --- a/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php @@ -74,7 +74,7 @@ public function getIntersect($dp = 0) * @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 { $adjustedYValues = array_map( function ($value) { @@ -104,7 +104,7 @@ public function __construct($yValues, $xValues = [], $const = true) parent::__construct($yValues, $xValues); if (!$this->error) { - $this->powerRegression($yValues, $xValues, $const); + $this->powerRegression($yValues, $xValues, (bool) $const); } } } diff --git a/src/PhpSpreadsheet/Shared/Trend/Trend.php b/src/PhpSpreadsheet/Shared/Trend/Trend.php index 1b7b390107..d0a117cbde 100644 --- a/src/PhpSpreadsheet/Shared/Trend/Trend.php +++ b/src/PhpSpreadsheet/Shared/Trend/Trend.php @@ -55,10 +55,9 @@ public static function calculate($trendType = self::TREND_BEST_FIT, $yValues = [ $nX = count($xValues); // Define X Values if necessary - if ($nX == 0) { + if ($nX === 0) { $xValues = range(1, $nY); - $nX = $nY; - } elseif ($nY != $nX) { + } elseif ($nY !== $nX) { // Ensure both arrays of points are the same size trigger_error('Trend(): Number of elements in coordinate arrays do not match.', E_USER_ERROR); } @@ -84,7 +83,7 @@ public static function calculate($trendType = self::TREND_BEST_FIT, $yValues = [ case self::TREND_POLYNOMIAL_6: if (!isset(self::$trendCache[$key])) { $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]; @@ -100,7 +99,7 @@ public static function calculate($trendType = self::TREND_BEST_FIT, $yValues = [ 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); + $bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues); if ($bestFit[$trendMethod]->getError()) { unset($bestFit[$trendMethod]); } else { From 18ccc7c19db4ad495cb619980281cb3962b88686 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 6 Mar 2021 18:02:49 +0100 Subject: [PATCH 10/18] Some minor refactoring --- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 37 ++++++++++++++------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/src/PhpSpreadsheet/Shared/Trend/BestFit.php index 3a1f0b1a1f..6d6f62838a 100644 --- a/src/PhpSpreadsheet/Shared/Trend/BestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -362,7 +362,7 @@ protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, } $this->SSResiduals = $SSres; - $this->DFResiduals = $this->valueCount - 1 - $const; + $this->DFResiduals = $this->valueCount - 1 - ($const === true ? 1 : 0); if ($this->DFResiduals == 0.0) { $this->stdevOfResiduals = 0.0; @@ -395,6 +395,18 @@ protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, } } + private function sumSquares(array $values) + { + return array_sum( + array_map( + function ($value) { + return $value ** 2; + }, + $values + ) + ); + } + /** * @param float[] $yValues * @param float[] $xValues @@ -402,19 +414,20 @@ protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, protected function leastSquareFit(array $yValues, array $xValues, bool $const): void { // 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; + $sumValuesX = array_sum($xValues); + $sumValuesY = array_sum($yValues); + $meanValueX = $sumValuesX / $this->valueCount; + $meanValueY = $sumValuesY / $this->valueCount; + $sumSquaresX = $this->sumSquares($xValues); + $sumSquaresY = $this->sumSquares($yValues); + $mBase = $mDivisor = 0.0; + $xy_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 === true) { - $mBase += ($xValues[$i] - $meanX) * ($yValues[$i] - $meanY); - $mDivisor += ($xValues[$i] - $meanX) * ($xValues[$i] - $meanX); + $mBase += ($xValues[$i] - $meanValueX) * ($yValues[$i] - $meanValueY); + $mDivisor += ($xValues[$i] - $meanValueX) * ($xValues[$i] - $meanValueX); } else { $mBase += $xValues[$i] * $yValues[$i]; $mDivisor += $xValues[$i] * $xValues[$i]; @@ -425,9 +438,9 @@ protected function leastSquareFit(array $yValues, array $xValues, bool $const): $this->slope = $mBase / $mDivisor; // calculate intersect - $this->intersect = ($const === true) ? $meanY - ($this->slope * $meanX) : 0.0; + $this->intersect = ($const === true) ? $meanValueY - ($this->slope * $meanValueX) : 0.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); } /** From 06f72267ef70fb081a392500f12b7b3259b3fe58 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 6 Mar 2021 19:06:10 +0100 Subject: [PATCH 11/18] More unit tests --- tests/data/Calculation/Statistical/LINEST.php | 7 +++++++ tests/data/Calculation/Statistical/LOGEST.php | 16 ++++++++++++++-- tests/data/Shared/Trend/LinearBestFit.php | 8 ++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/data/Calculation/Statistical/LINEST.php b/tests/data/Calculation/Statistical/LINEST.php index ff1913bc56..ee5fdb0bde 100644 --- a/tests/data/Calculation/Statistical/LINEST.php +++ b/tests/data/Calculation/Statistical/LINEST.php @@ -3,6 +3,13 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions; 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], diff --git a/tests/data/Calculation/Statistical/LOGEST.php b/tests/data/Calculation/Statistical/LOGEST.php index 38a2ff8d75..bba7487b97 100644 --- a/tests/data/Calculation/Statistical/LOGEST.php +++ b/tests/data/Calculation/Statistical/LOGEST.php @@ -1,8 +1,20 @@ [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], + ], ]; From f94dbf57694e175df4467d1904cd0d503e67764a Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 6 Mar 2021 19:09:18 +0100 Subject: [PATCH 12/18] PHPCS --- src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php | 1 - src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php | 1 - src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php | 1 - 3 files changed, 3 deletions(-) diff --git a/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php b/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php index 570a12ab17..eb8cd746d3 100644 --- a/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php @@ -88,7 +88,6 @@ public function getIntersect($dp = 0) * * @param float[] $yValues The set of Y-values for this regression * @param float[] $xValues The set of X-values for this regression - * @param bool $const */ private function exponentialRegression(array $yValues, array $xValues, bool $const): void { diff --git a/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php b/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php index 2208447181..65d6b4ff44 100644 --- a/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php @@ -56,7 +56,6 @@ public function getEquation($dp = 0) * * @param float[] $yValues The set of Y-values for this regression * @param float[] $xValues The set of X-values for this regression - * @param bool $const */ private function linearRegression(array $yValues, array $xValues, bool $const): void { diff --git a/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php b/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php index e273263447..2366dc636a 100644 --- a/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php @@ -56,7 +56,6 @@ public function getEquation($dp = 0) * * @param float[] $yValues The set of Y-values for this regression * @param float[] $xValues The set of X-values for this regression - * @param bool $const */ private function logarithmicRegression(array $yValues, array $xValues, bool $const): void { From 3b92899a28c3b48550e459c99c337e2c10290e06 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 6 Mar 2021 19:19:10 +0100 Subject: [PATCH 13/18] PHPCS --- src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php b/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php index 46ee8c6a14..cafd01158e 100644 --- a/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php @@ -72,7 +72,6 @@ public function getIntersect($dp = 0) * * @param float[] $yValues The set of Y-values for this regression * @param float[] $xValues The set of X-values for this regression - * @param bool $const */ private function powerRegression(array $yValues, array $xValues, bool $const): void { From 73d400919e9db0e051eead835e91fcbd36e287db Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 6 Mar 2021 23:04:38 +0100 Subject: [PATCH 14/18] Minor typing improvements --- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 7 ------- src/PhpSpreadsheet/Shared/Trend/Trend.php | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/src/PhpSpreadsheet/Shared/Trend/BestFit.php index 6d6f62838a..693bf2d8f4 100644 --- a/src/PhpSpreadsheet/Shared/Trend/BestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -39,13 +39,6 @@ class BestFit */ protected $yValues = []; - /** - * Flag indicating whether values should be adjusted to Y=0. - * - * @var bool - */ - protected $adjustToZero = false; - /** * Y-value series of best-fit values. * diff --git a/src/PhpSpreadsheet/Shared/Trend/Trend.php b/src/PhpSpreadsheet/Shared/Trend/Trend.php index d0a117cbde..fc8e9a2cc0 100644 --- a/src/PhpSpreadsheet/Shared/Trend/Trend.php +++ b/src/PhpSpreadsheet/Shared/Trend/Trend.php @@ -96,7 +96,7 @@ public static function calculate($trendType = self::TREND_BEST_FIT, $yValues = [ $bestFit[$trendMethod] = new $className($yValues, $xValues, $const); $bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit(); } - if ($trendType != self::TREND_BEST_FIT_NO_POLY) { + if ($trendType !== self::TREND_BEST_FIT_NO_POLY) { foreach (self::$trendTypePolynomialOrders as $trendMethod) { $order = substr($trendMethod, -1); $bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues); From 6fb8151b6671e298cf7bfc7b347aec47a19d42b9 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sun, 7 Mar 2021 15:39:17 +0100 Subject: [PATCH 15/18] Improvements to BestFit --- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 101 +++++++------------- 1 file changed, 34 insertions(+), 67 deletions(-) diff --git a/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/src/PhpSpreadsheet/Shared/Trend/BestFit.php index 693bf2d8f4..e9869cdd26 100644 --- a/src/PhpSpreadsheet/Shared/Trend/BestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -2,7 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Shared\Trend; -class BestFit +abstract class BestFit { /** * Indicator flag for a calculation error. @@ -89,24 +89,18 @@ public function getBestFitType() * * @param float $xValue X-Value * - * @return bool Y-Value + * @return float Y-Value */ - public function getValueOfYForX($xValue) - { - return false; - } + abstract public function getValueOfYForX($xValue); /** * Return the X-Value for a specified value of Y. * * @param float $yValue Y-Value * - * @return bool X-Value + * @return float X-Value */ - public function getValueOfXForY($yValue) - { - return false; - } + abstract public function getValueOfXForY($yValue); /** * Return the original set of X-Values. @@ -123,12 +117,9 @@ public function getXValues() * * @param int $dp Number of places of decimal precision to display * - * @return bool + * @return string */ - public function getEquation($dp = 0) - { - return false; - } + abstract public function getEquation($dp = 0); /** * Return the Slope of the line. @@ -271,16 +262,10 @@ public function getSSResiduals($dp = 0) } /** - * @param int $dp Number of places of decimal precision to return - * - * @return float + * @return int */ - public function getDFResiduals($dp = 0) + public function getDFResiduals() { - if ($dp != 0) { - return round($this->DFResiduals, $dp); - } - return $this->DFResiduals; } @@ -336,55 +321,37 @@ public function getYBestFitValues() protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void { - $SSres = $SScov = $SScor = $SStot = $SSsex = 0.0; + $SSresiduals = $SScovariance = $SScorrelation = $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 === true) { - $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 === true) { - $SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX); - } else { - $SSsex += $this->xValues[$xKey] * $this->xValues[$xKey]; - } + $SSresiduals += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY); + $SStot += ($const === true) + ? ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY) + : $this->yValues[$xKey] * $this->yValues[$xKey]; + $SScovariance += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY); + $SSsex += ($const === true) + ? ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX) + : $this->xValues[$xKey] * $this->xValues[$xKey]; } - $this->SSResiduals = $SSres; + $this->SSResiduals = $SSresiduals; $this->DFResiduals = $this->valueCount - 1 - ($const === true ? 1 : 0); - - 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->stdevOfResiduals = ($this->DFResiduals == 0.0) ? 0.0 : sqrt($SSresiduals / $this->DFResiduals); + $this->goodnessOfFit = (($SStot == 0.0) || ($SSresiduals == $SStot)) ? 1.0 : 1.0 - ($SSresiduals / $SStot); $this->SSRegression = $this->goodnessOfFit * $SStot; - $this->covariance = $SScov / $this->valueCount; + $this->covariance = $SScovariance / $this->valueCount; $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - $sumX ** 2) * ($this->valueCount * $sumY2 - $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); - } + $this->f = ($this->DFResiduals == 0.0) + ? 0.0 + : $this->SSRegression / ($this->SSResiduals / $this->DFResiduals); } else { - if ($this->DFResiduals == 0.0) { - $this->f = 0.0; - } else { - $this->f = $this->SSRegression / $this->DFResiduals; - } + $this->f = ($this->DFResiduals == 0.0) ? 0.0 : $this->SSRegression / $this->DFResiduals; } } @@ -413,18 +380,18 @@ protected function leastSquareFit(array $yValues, array $xValues, bool $const): $meanValueY = $sumValuesY / $this->valueCount; $sumSquaresX = $this->sumSquares($xValues); $sumSquaresY = $this->sumSquares($yValues); + $mBase = $mDivisor = 0.0; $xy_sum = 0.0; for ($i = 0; $i < $this->valueCount; ++$i) { $xy_sum += $xValues[$i] * $yValues[$i]; - if ($const === true) { - $mBase += ($xValues[$i] - $meanValueX) * ($yValues[$i] - $meanValueY); - $mDivisor += ($xValues[$i] - $meanValueX) * ($xValues[$i] - $meanValueX); - } else { - $mBase += $xValues[$i] * $yValues[$i]; - $mDivisor += $xValues[$i] * $xValues[$i]; - } + $mBase += ($const === true) + ? ($xValues[$i] - $meanValueX) * ($yValues[$i] - $meanValueY) + : $xValues[$i] * $yValues[$i]; + $mDivisor += ($const === true) + ? ($xValues[$i] - $meanValueX) * ($xValues[$i] - $meanValueX) + : $xValues[$i] * $xValues[$i]; } // calculate slope From 37bca855ee1ffeb3662a571db92fa3243d15ca81 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sun, 7 Mar 2021 15:45:08 +0100 Subject: [PATCH 16/18] Resolve merge conflicts --- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/src/PhpSpreadsheet/Shared/Trend/BestFit.php index a9fc8e4855..e9869cdd26 100644 --- a/src/PhpSpreadsheet/Shared/Trend/BestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -367,18 +367,6 @@ function ($value) { ); } - private function sumSquares(array $values) - { - return array_sum( - array_map( - function ($value) { - return $value ** 2; - }, - $values - ) - ); - } - /** * @param float[] $yValues * @param float[] $xValues From 6d613cd43cd08824b431f8d657fa6e5afeaa82bf Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sun, 7 Mar 2021 16:22:54 +0100 Subject: [PATCH 17/18] Some refactoring of BestFit class --- src/PhpSpreadsheet/Shared/Trend/BestFit.php | 84 +++++++++++-------- .../Shared/Trend/PolynomialBestFit.php | 2 +- .../Shared/Trend/LogarithmicBestFitTest.php | 49 +++++++++++ .../data/Shared/Trend/LogarithmicBestFit.php | 12 +++ 4 files changed, 111 insertions(+), 36 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Shared/Trend/LogarithmicBestFitTest.php create mode 100644 tests/data/Shared/Trend/LogarithmicBestFit.php diff --git a/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/src/PhpSpreadsheet/Shared/Trend/BestFit.php index e9869cdd26..4efb109d0b 100644 --- a/src/PhpSpreadsheet/Shared/Trend/BestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/BestFit.php @@ -319,52 +319,68 @@ public function getYBestFitValues() return $this->yBestFitValues; } - protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void + private function sumSquares(array $values): float { - $SSresiduals = $SScovariance = $SScorrelation = $SStot = $SSsex = 0.0; - foreach ($this->xValues as $xKey => $xValue) { - $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue); + return array_sum( + array_map( + function ($value) { + return $value ** 2; + }, + $values + ) + ); + } - $SSresiduals += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY); - $SStot += ($const === true) - ? ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY) - : $this->yValues[$xKey] * $this->yValues[$xKey]; - $SScovariance += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY); - $SSsex += ($const === true) - ? ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX) - : $this->xValues[$xKey] * $this->xValues[$xKey]; - } + /** + * @param $sumXY + * @param $sumValuesX + * @param $sumValuesY + */ + protected function calculateGoodnessOfFit(float $SStot, $sumXY, $sumValuesX, $sumValuesY, float $SSsex): void + { + $sumSquaresX = $this->sumSquares($this->xValues); + $sumSquaresY = $this->sumSquares($this->yValues); - $this->SSResiduals = $SSresiduals; - $this->DFResiduals = $this->valueCount - 1 - ($const === true ? 1 : 0); - $this->stdevOfResiduals = ($this->DFResiduals == 0.0) ? 0.0 : sqrt($SSresiduals / $this->DFResiduals); - $this->goodnessOfFit = (($SStot == 0.0) || ($SSresiduals == $SStot)) ? 1.0 : 1.0 - ($SSresiduals / $SStot); + $this->stdevOfResiduals = ($this->DFResiduals == 0.0) ? 0.0 : sqrt($this->SSResiduals / $this->DFResiduals); + $this->goodnessOfFit = (($SStot == 0.0) || ($this->SSResiduals == $SStot)) + ? 1.0 + : 1.0 - ($this->SSResiduals / $SStot); $this->SSRegression = $this->goodnessOfFit * $SStot; - $this->covariance = $SScovariance / $this->valueCount; - $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - $sumX ** 2) * ($this->valueCount * $sumY2 - $sumY ** 2)); + $this->correlation = ($this->valueCount * $sumXY - $sumValuesX * $sumValuesY) / + sqrt(($this->valueCount * $sumSquaresX - $sumValuesX ** 2) * ($this->valueCount * $sumSquaresY - $sumValuesY ** 2)); $this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex); - $this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2)); + $this->intersectSE = $this->stdevOfResiduals * + sqrt(1 / ($this->valueCount - ($sumValuesX * $sumValuesX) / $sumSquaresX)); if ($this->SSResiduals != 0.0) { - $this->f = ($this->DFResiduals == 0.0) - ? 0.0 - : $this->SSRegression / ($this->SSResiduals / $this->DFResiduals); + $this->f = ($this->DFResiduals == 0.0) ? 0.0 : $this->SSRegression / ($this->SSResiduals / $this->DFResiduals); } else { $this->f = ($this->DFResiduals == 0.0) ? 0.0 : $this->SSRegression / $this->DFResiduals; } } - private function sumSquares(array $values) + protected function calculateResiduals($sumValuesX, $sumValuesY, $sumXY, $meanValueX, $meanYmeanValueY, $const): void { - return array_sum( - array_map( - function ($value) { - return $value ** 2; - }, - $values - ) - ); + $SSresiduals = $SScovariance = $SStot = $SSsex = 0.0; + foreach ($this->xValues as $xKey => $xValue) { + $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue); + + $SSresiduals += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY); + $SStot += ($const === true) + ? ($this->yValues[$xKey] - $meanYmeanValueY) * ($this->yValues[$xKey] - $meanYmeanValueY) + : $this->yValues[$xKey] * $this->yValues[$xKey]; + $SScovariance += ($this->xValues[$xKey] - $meanValueX) * ($this->yValues[$xKey] - $meanYmeanValueY); + $SSsex += ($const === true) + ? ($this->xValues[$xKey] - $meanValueX) * ($this->xValues[$xKey] - $meanValueX) + : $this->xValues[$xKey] * $this->xValues[$xKey]; + } + + $this->DFResiduals = $this->valueCount - 1 - ($const === true ? 1 : 0); + $this->SSResiduals = $SSresiduals; + $this->covariance = $SScovariance / $this->valueCount; + + $this->calculateGoodnessOfFit($SStot, $sumXY, $sumValuesX, $sumValuesY, $SSsex); } /** @@ -378,8 +394,6 @@ protected function leastSquareFit(array $yValues, array $xValues, bool $const): $sumValuesY = array_sum($yValues); $meanValueX = $sumValuesX / $this->valueCount; $meanValueY = $sumValuesY / $this->valueCount; - $sumSquaresX = $this->sumSquares($xValues); - $sumSquaresY = $this->sumSquares($yValues); $mBase = $mDivisor = 0.0; $xy_sum = 0.0; @@ -400,7 +414,7 @@ protected function leastSquareFit(array $yValues, array $xValues, bool $const): // calculate intersect $this->intersect = ($const === true) ? $meanValueY - ($this->slope * $meanValueX) : 0.0; - $this->calculateGoodnessOfFit($sumValuesX, $sumValuesY, $sumSquaresX, $sumSquaresY, $xy_sum, $meanValueX, $meanValueY, $const); + $this->calculateResiduals($sumValuesX, $sumValuesY, $xy_sum, $meanValueX, $meanValueY, $const); } /** diff --git a/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php b/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php index 1d34e81c19..feb382ab91 100644 --- a/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php +++ b/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php @@ -166,7 +166,7 @@ private function polynomialRegression($order, $yValues, $xValues): void $this->intersect = array_shift($coefficients); $this->slope = $coefficients; - $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0); + $this->calculateResiduals($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0); foreach ($this->xValues as $xKey => $xValue) { $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue); } diff --git a/tests/PhpSpreadsheetTests/Shared/Trend/LogarithmicBestFitTest.php b/tests/PhpSpreadsheetTests/Shared/Trend/LogarithmicBestFitTest.php new file mode 100644 index 0000000000..eef5ffc961 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Shared/Trend/LogarithmicBestFitTest.php @@ -0,0 +1,49 @@ +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 providerLogarithmicBestFit() + { + return require 'tests/data/Shared/Trend/LogarithmicBestFit.php'; + } +} diff --git a/tests/data/Shared/Trend/LogarithmicBestFit.php b/tests/data/Shared/Trend/LogarithmicBestFit.php new file mode 100644 index 0000000000..98d91004ad --- /dev/null +++ b/tests/data/Shared/Trend/LogarithmicBestFit.php @@ -0,0 +1,12 @@ + [-4.9, -4.918384677001153], + 'intersect' => [15.1, 15.081824008436346], + 'goodnessOfFit' => [0.949372, 0.949372], + '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], + ], +]; From 69c77d980f2bae2cefc7c8c71d52a6a905718b2e Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sun, 7 Mar 2021 16:24:26 +0100 Subject: [PATCH 18/18] Some refactoring of BestFit class --- .../Shared/Trend/LogarithmicBestFitTest.php | 49 ------------------- .../data/Shared/Trend/LogarithmicBestFit.php | 12 ----- 2 files changed, 61 deletions(-) delete mode 100644 tests/PhpSpreadsheetTests/Shared/Trend/LogarithmicBestFitTest.php delete mode 100644 tests/data/Shared/Trend/LogarithmicBestFit.php diff --git a/tests/PhpSpreadsheetTests/Shared/Trend/LogarithmicBestFitTest.php b/tests/PhpSpreadsheetTests/Shared/Trend/LogarithmicBestFitTest.php deleted file mode 100644 index eef5ffc961..0000000000 --- a/tests/PhpSpreadsheetTests/Shared/Trend/LogarithmicBestFitTest.php +++ /dev/null @@ -1,49 +0,0 @@ -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 providerLogarithmicBestFit() - { - return require 'tests/data/Shared/Trend/LogarithmicBestFit.php'; - } -} diff --git a/tests/data/Shared/Trend/LogarithmicBestFit.php b/tests/data/Shared/Trend/LogarithmicBestFit.php deleted file mode 100644 index 98d91004ad..0000000000 --- a/tests/data/Shared/Trend/LogarithmicBestFit.php +++ /dev/null @@ -1,12 +0,0 @@ - [-4.9, -4.918384677001153], - 'intersect' => [15.1, 15.081824008436346], - 'goodnessOfFit' => [0.949372, 0.949372], - '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], - ], -];