From a982bb432a49ff8377eb4171e0dbc776a633f8fd Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:29:53 +0100 Subject: [PATCH 01/13] Started to implement tests --- tests/Math/Statistic/Forecast/ErrorTest.php | 38 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/Math/Statistic/Forecast/ErrorTest.php b/tests/Math/Statistic/Forecast/ErrorTest.php index fb3d36c55..7aace9f8b 100644 --- a/tests/Math/Statistic/Forecast/ErrorTest.php +++ b/tests/Math/Statistic/Forecast/ErrorTest.php @@ -17,8 +17,42 @@ use phpOMS\Math\Statistic\Forecast\Error; class ErrorTest extends \PHPUnit\Framework\TestCase { - public function testPlaceholder() + public function testForecastError() { - self::markTestIncomplete(); + self::assertEquals(1000 - 700, Error::getForecastError(1000, 700)); + + self::assertEquals( + [ + 400 - 300, + 600 - 700, + 200 - 200, + 500 - -300 + ], + Error::getForecastErrorArray( + [400, 600, 200, 500], + [300, 700, 200, -300] + ) + ); + } + + public function testErrorPercentage() + { + self::assertEquals(300 / 1000, Error::getPercentageError(300, 1000), '', 0.01); + + self::assertEquals( + [ + (400 - 300) / 400, + (600 - 700) / 600, + (200 - 200) / 200, + (500 - -300) / 500 + ], + Error::getPercentageErrorArray( + Error::getForecastErrorArray( + [400, 600, 200, 500], + [300, 700, 200, -300] + ), + [400, 600, 200, 500] + ) + ); } } From 3f438238e7cc8ce2e635f3b13533c5af244fa05a Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:34:01 +0100 Subject: [PATCH 02/13] Added power function on arrays --- Math/Functions/Functions.php | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Math/Functions/Functions.php b/Math/Functions/Functions.php index 82e96cf0f..e2aa19840 100644 --- a/Math/Functions/Functions.php +++ b/Math/Functions/Functions.php @@ -241,6 +241,50 @@ class Functions { return !((bool) ($a & 1)); } + + /** + * Power all values in array. + * + * @param array $values Values to square + * @param float $exp Exponent + * + * @return array + * + * @since 1.0.0 + * todo: move to utils?! implement sqrt for array as well... could be usefull for others (e.g. matrix) + */ + public static function powerFloat(array $values, float $exp = 2.0) : array + { + $squared = []; + + foreach ($values as $value) { + $squared[] = $value * $exp; + } + + return $squared; + } + + /** + * Power all values in array. + * + * @param array $values Values to square + * @param int $exp Exponent + * + * @return array + * + * @since 1.0.0 + * todo: move to utils?! implement sqrt for array as well... could be usefull for others (e.g. matrix) + */ + public static function powerInt(array $values, int $exp = 2) : array + { + $squared = []; + + foreach ($values as $value) { + $squared[] = $value * $exp; + } + + return $squared; + } /** * Gets the relative position on a circular construct. From a1b78de31273d93100d523ea96305065f584d9fd Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:35:16 +0100 Subject: [PATCH 03/13] Pulled out square function --- Math/Statistic/Forecast/Error.php | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/Math/Statistic/Forecast/Error.php b/Math/Statistic/Forecast/Error.php index 238cafb28..74afcc5ab 100644 --- a/Math/Statistic/Forecast/Error.php +++ b/Math/Statistic/Forecast/Error.php @@ -125,7 +125,7 @@ class Error */ public static function getMeanSquaredError(array $errors) : float { - return Average::arithmeticMean(self::square($errors)); + return Average::arithmeticMean(Functions::powerInt($errors, 2)); } /** @@ -139,7 +139,7 @@ class Error */ public static function getRootMeanSquaredError(array $errors) : float { - return sqrt(Average::arithmeticMean(self::square($errors))); + return sqrt(Average::arithmeticMean(Functions::powerInt($errors, 2))); } /** @@ -297,27 +297,6 @@ class Error return Average::arithmeticMean($error); } - /** - * Square all values in array. - * - * @param array $values Values to square - * - * @return array - * - * @since 1.0.0 - * todo: move to utils?! implement sqrt for array as well... could be usefull for others (e.g. matrix) - */ - private static function square(array $values) : array - { - $squared = []; - - foreach ($values as $value) { - $squared[] = $value * $value; - } - - return $squared; - } - /** * Get cross sectional scaled errors (CSSE) * @@ -387,7 +366,7 @@ class Error */ public static function getMeanSquaredScaledError(array $scaledErrors) : float { - return Average::arithmeticMean(self::square($scaledErrors)); + return Average::arithmeticMean(Functions::powerInt($scaledErrors, 2)); } /** From bf9625fc0b883d4ba886a13f57f44ce096f80e86 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:36:13 +0100 Subject: [PATCH 04/13] Update Functions.php --- Math/Functions/Functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Math/Functions/Functions.php b/Math/Functions/Functions.php index e2aa19840..82d9ff418 100644 --- a/Math/Functions/Functions.php +++ b/Math/Functions/Functions.php @@ -15,7 +15,7 @@ declare(strict_types=1); namespace phpOMS\Math\Functions; /** - * Well known functions class. + * Well known functions and helpers class. * * @package Framework * @license OMS License 1.0 From b3b73917127f48cfb381c0b54d0a1a8302b30209 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:39:14 +0100 Subject: [PATCH 05/13] Added power test --- tests/Math/Functions/FunctionsTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/Math/Functions/FunctionsTest.php b/tests/Math/Functions/FunctionsTest.php index f88839b89..517231cb1 100644 --- a/tests/Math/Functions/FunctionsTest.php +++ b/tests/Math/Functions/FunctionsTest.php @@ -68,4 +68,29 @@ class FunctionsTest extends \PHPUnit\Framework\TestCase self::assertEquals(5, Functions::getRelativeDegree(12, 12, 7)); self::assertEquals(11, Functions::getRelativeDegree(6, 12, 7)); } + + public function testPower() + { + self::assertEquals( + [4, 9, 16], + Functions::powerInt([2, 3, 4], 2) + ); + + self::assertEquals( + [8, 27, 64], + Functions::powerInt([2, 3, 4], 3) + ); + + self::assertEquals( + [2, 3, 8], + Functions::powerInt([4, 9, 16], 1/2), + '', 0.0 + ); + + self::assertEquals( + [2, 3, 8], + Functions::powerInt([8, 27, 64], 1/3), + '', 0.0 + ); + } } From cc682b1945fa720f2804d5478a00c66d9896a5c6 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:49:00 +0100 Subject: [PATCH 06/13] Added mean error test --- tests/Math/Statistic/Forecast/ErrorTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/Math/Statistic/Forecast/ErrorTest.php b/tests/Math/Statistic/Forecast/ErrorTest.php index 7aace9f8b..0ffcad770 100644 --- a/tests/Math/Statistic/Forecast/ErrorTest.php +++ b/tests/Math/Statistic/Forecast/ErrorTest.php @@ -55,4 +55,18 @@ class ErrorTest extends \PHPUnit\Framework\TestCase ) ); } + + public function testMeanError() + { + $errors = [ + 400 - 300, + 600 - 700, + 200 - 200, + 500 - -300 + ]; + + self::assertEquals(300, Error::getMeanAbsoulteError($errors), '', 0.01); + self::assertEquals(125000, Error::getMeanSquaredError($errors), '', 0.01); + self::assertEquals(406.2019, Error::getRootMeanSquaredError($errors), '', 0.01); + } } From 59b701a6be9a479aa261a0f44eee64203bf78f30 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:54:58 +0100 Subject: [PATCH 07/13] Fix formatting --- tests/Math/Functions/FunctionsTest.php | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/tests/Math/Functions/FunctionsTest.php b/tests/Math/Functions/FunctionsTest.php index 517231cb1..ae5f43cb2 100644 --- a/tests/Math/Functions/FunctionsTest.php +++ b/tests/Math/Functions/FunctionsTest.php @@ -71,26 +71,10 @@ class FunctionsTest extends \PHPUnit\Framework\TestCase public function testPower() { - self::assertEquals( - [4, 9, 16], - Functions::powerInt([2, 3, 4], 2) - ); + self::assertEquals([4, 9, 16], Functions::powerInt([2, 3, 4], 2)); + self::assertEquals([8, 27, 64], Functions::powerInt([2, 3, 4], 3)); - self::assertEquals( - [8, 27, 64], - Functions::powerInt([2, 3, 4], 3) - ); - - self::assertEquals( - [2, 3, 8], - Functions::powerInt([4, 9, 16], 1/2), - '', 0.0 - ); - - self::assertEquals( - [2, 3, 8], - Functions::powerInt([8, 27, 64], 1/3), - '', 0.0 - ); + self::assertEquals([2, 3, 8], Functions::powerInt([4, 9, 16], 1/2), '', 0.0); + self::assertEquals([2, 3, 8], Functions::powerInt([8, 27, 64], 1/3), '', 0.0); } } From 66a5510873f0761a9c1fcd066514b3baa656603f Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:55:35 +0100 Subject: [PATCH 08/13] Fix int/float bug --- tests/Math/Functions/FunctionsTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Math/Functions/FunctionsTest.php b/tests/Math/Functions/FunctionsTest.php index ae5f43cb2..7dc9e2aed 100644 --- a/tests/Math/Functions/FunctionsTest.php +++ b/tests/Math/Functions/FunctionsTest.php @@ -74,7 +74,7 @@ class FunctionsTest extends \PHPUnit\Framework\TestCase self::assertEquals([4, 9, 16], Functions::powerInt([2, 3, 4], 2)); self::assertEquals([8, 27, 64], Functions::powerInt([2, 3, 4], 3)); - self::assertEquals([2, 3, 8], Functions::powerInt([4, 9, 16], 1/2), '', 0.0); - self::assertEquals([2, 3, 8], Functions::powerInt([8, 27, 64], 1/3), '', 0.0); + self::assertEquals([2, 3, 8], Functions::powerFloat([4, 9, 16], 1/2), '', 0.0); + self::assertEquals([2, 3, 8], Functions::powerFloat([8, 27, 64], 1/3), '', 0.0); } } From a08c63a021509f2c8dd3bb706a11b0b996a4980c Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 10:56:35 +0100 Subject: [PATCH 09/13] Formatting fix --- tests/Math/Statistic/Forecast/ErrorTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Math/Statistic/Forecast/ErrorTest.php b/tests/Math/Statistic/Forecast/ErrorTest.php index 0ffcad770..cb08f19a9 100644 --- a/tests/Math/Statistic/Forecast/ErrorTest.php +++ b/tests/Math/Statistic/Forecast/ErrorTest.php @@ -20,7 +20,6 @@ class ErrorTest extends \PHPUnit\Framework\TestCase public function testForecastError() { self::assertEquals(1000 - 700, Error::getForecastError(1000, 700)); - self::assertEquals( [ 400 - 300, @@ -38,7 +37,6 @@ class ErrorTest extends \PHPUnit\Framework\TestCase public function testErrorPercentage() { self::assertEquals(300 / 1000, Error::getPercentageError(300, 1000), '', 0.01); - self::assertEquals( [ (400 - 300) / 400, From 5185b52c919910ba07473b31a8187bb9c4ab281a Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 11:12:07 +0100 Subject: [PATCH 10/13] Added optional mean value Now it is possible to pass pre calculated means in case the arithmeticMean shouldn't be used --- Math/Statistic/MeasureOfDispersion.php | 53 +++++++++++++++----------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/Math/Statistic/MeasureOfDispersion.php b/Math/Statistic/MeasureOfDispersion.php index ea7e5b7ac..fb05e548c 100644 --- a/Math/Statistic/MeasureOfDispersion.php +++ b/Math/Statistic/MeasureOfDispersion.php @@ -54,6 +54,7 @@ class MeasureOfDispersion * Example: ([4, 5, 9, 1, 3]) * * @param array $values Values + * @param float $mean Mean * * @return float * @@ -61,9 +62,9 @@ class MeasureOfDispersion * * @since 1.0.0 */ - public static function empiricalVariationcoefficient(array $values) : float + public static function empiricalVariationcoefficient(array $values, float $mean = null) : float { - $mean = Average::arithmeticMean($values); + $mean = isset($mean) ? $mean : Average::arithmeticMean($values); if ($mean === 0) { throw new ZeroDevisionException(); @@ -78,14 +79,15 @@ class MeasureOfDispersion * Example: ([4, 5, 9, 1, 3]) * * @param array $values Values + * @param float $mean Mean * * @return float * * @since 1.0.0 */ - public static function standardDeviation(array $values) : float + public static function standardDeviation(array $values, float $mean = null) : float { - $mean = Average::arithmeticMean($values); + $mean = isset($mean) ? $mean : Average::arithmeticMean($values); $sum = 0.0; foreach ($values as $value) { @@ -101,6 +103,7 @@ class MeasureOfDispersion * Example: ([4, 5, 9, 1, 3]) * * @param array $values Values + * @param float $mean Mean * * @return float * @@ -108,7 +111,7 @@ class MeasureOfDispersion * * @since 1.0.0 */ - public static function sampleVariance(array $values) : float + public static function sampleVariance(array $values, float $mean = null) : float { $count = count($values); @@ -116,7 +119,7 @@ class MeasureOfDispersion throw new ZeroDevisionException(); } - return self::empiricalVariance($values) * $count / ($count - 1); + return self::empiricalVariance($values, [], $mean) * $count / ($count - 1); } /** @@ -126,6 +129,7 @@ class MeasureOfDispersion * * @param array $values Values * @param array $probabilities Probabilities + * @param float $mean Mean * * @return float * @@ -133,7 +137,7 @@ class MeasureOfDispersion * * @since 1.0.0 */ - public static function empiricalVariance(array $values, array $probabilities = []) : float + public static function empiricalVariance(array $values, array $probabilities = [], float $mean = null) : float { $count = count($values); $hasProbability = !empty($probabilities); @@ -142,7 +146,7 @@ class MeasureOfDispersion throw new ZeroDevisionException(); } - $mean = $hasProbability ? Average::weightedAverage($values, $probabilities) : Average::arithmeticMean($values); + $mean = $hasProbability ? Average::weightedAverage($values, $probabilities) : (isset($mean) ? $mean : Average::arithmeticMean($values);); $sum = 0; foreach ($values as $key => $value) { @@ -157,8 +161,10 @@ class MeasureOfDispersion * * Example: ([4, 5, 9, 1, 3], [4, 5, 9, 1, 3]) * - * @param array $x Values - * @param array $y Values + * @param array $x Values + * @param array $y Values + * @param array $meanX Mean + * @param array $meanY Mean * * @return float * @@ -166,7 +172,7 @@ class MeasureOfDispersion * * @since 1.0.0 */ - public static function empiricalCovariance(array $x, array $y) : float + public static function empiricalCovariance(array $x, array $y, float $meanX = null, float $meanY = null) : float { $count = count($x); @@ -178,8 +184,8 @@ class MeasureOfDispersion throw new InvalidDimensionException($count . 'x' . count($y)); } - $xMean = Average::arithmeticMean($x); - $yMean = Average::arithmeticMean($y); + $xMean = isset($meanX) ? $meanX : Average::arithmeticMean($x); + $yMean = isset($meanY) ? $meanY : Average::arithmeticMean($y); $sum = 0.0; @@ -207,15 +213,16 @@ class MeasureOfDispersion /** * Get mean deviation. * - * @param array $x Values + * @param array $x Values + * @param float $mean Mean * * @return float * * @since 1.0.0 */ - public static function meanDeviation(array $x) : float + public static function meanDeviation(array $x, float $mean = null) : float { - $mean = Average::arithmeticMean($x); + $mean = isset($mean) ? $mean : Average::arithmeticMean($x); $sum = 0.0; foreach ($x as $xi) { @@ -228,15 +235,16 @@ class MeasureOfDispersion /** * Get mean absolute deviation. * - * @param array $x Values + * @param array $x Values + * @param float $mean Mean * * @return float * * @since 1.0.0 */ - public static function meanAbsoluteDeviation(array $x) : float + public static function meanAbsoluteDeviation(array $x, float $mean = null) : float { - $mean = Average::arithmeticMean($x); + $mean = isset($mean) ? $mean : Average::arithmeticMean($x); $sum = 0.0; foreach ($x as $xi) { @@ -249,15 +257,16 @@ class MeasureOfDispersion /** * Get squared mean deviation. * - * @param array $x Values + * @param array $x Values + * @param float $mean Mean * * @return float * * @since 1.0.0 */ - public static function squaredMeanDeviation(array $x) : float + public static function squaredMeanDeviation(array $x, float $mean = null) : float { - $mean = Average::arithmeticMean($x); + $mean = isset($mean) ? $mean : Average::arithmeticMean($x); $sum = 0.0; foreach ($x as $xi) { From e97bd27eb03323d51fb6ddc786573172b996bb9f Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 11:12:53 +0100 Subject: [PATCH 11/13] Semicolon bug fix --- Math/Statistic/MeasureOfDispersion.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Math/Statistic/MeasureOfDispersion.php b/Math/Statistic/MeasureOfDispersion.php index fb05e548c..8a7170e1c 100644 --- a/Math/Statistic/MeasureOfDispersion.php +++ b/Math/Statistic/MeasureOfDispersion.php @@ -146,7 +146,7 @@ class MeasureOfDispersion throw new ZeroDevisionException(); } - $mean = $hasProbability ? Average::weightedAverage($values, $probabilities) : (isset($mean) ? $mean : Average::arithmeticMean($values);); + $mean = $hasProbability ? Average::weightedAverage($values, $probabilities) : (isset($mean) ? $mean : Average::arithmeticMean($values)); $sum = 0; foreach ($values as $key => $value) { From 03208332ae186460a5f4a2e2a7464dd886e6d738 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 17:55:17 +0100 Subject: [PATCH 12/13] Fix error bugs --- Math/Functions/Functions.php | 4 ++-- Math/Statistic/Forecast/Error.php | 4 ++-- tests/Math/Functions/FunctionsTest.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Math/Functions/Functions.php b/Math/Functions/Functions.php index 82d9ff418..79ca16a8a 100644 --- a/Math/Functions/Functions.php +++ b/Math/Functions/Functions.php @@ -258,7 +258,7 @@ class Functions $squared = []; foreach ($values as $value) { - $squared[] = $value * $exp; + $squared[] = $value ** $exp; } return $squared; @@ -280,7 +280,7 @@ class Functions $squared = []; foreach ($values as $value) { - $squared[] = $value * $exp; + $squared[] = $value ** $exp; } return $squared; diff --git a/Math/Statistic/Forecast/Error.php b/Math/Statistic/Forecast/Error.php index 74afcc5ab..e8c0ff10b 100644 --- a/Math/Statistic/Forecast/Error.php +++ b/Math/Statistic/Forecast/Error.php @@ -111,7 +111,7 @@ class Error */ public static function getMeanAbsoulteError(array $errors) : float { - return Average::arithmeticMean(Functions::abs($errors)); + return MeasureOfDispersion::meanAbsoluteDeviation($errors); } /** @@ -125,7 +125,7 @@ class Error */ public static function getMeanSquaredError(array $errors) : float { - return Average::arithmeticMean(Functions::powerInt($errors, 2)); + return MeasureOfDispersion::squaredMeanDeviation($errors); } /** diff --git a/tests/Math/Functions/FunctionsTest.php b/tests/Math/Functions/FunctionsTest.php index 7dc9e2aed..9fa493b10 100644 --- a/tests/Math/Functions/FunctionsTest.php +++ b/tests/Math/Functions/FunctionsTest.php @@ -74,7 +74,7 @@ class FunctionsTest extends \PHPUnit\Framework\TestCase self::assertEquals([4, 9, 16], Functions::powerInt([2, 3, 4], 2)); self::assertEquals([8, 27, 64], Functions::powerInt([2, 3, 4], 3)); - self::assertEquals([2, 3, 8], Functions::powerFloat([4, 9, 16], 1/2), '', 0.0); - self::assertEquals([2, 3, 8], Functions::powerFloat([8, 27, 64], 1/3), '', 0.0); + self::assertEquals([2.0, 3.0, 4.0], Functions::powerFloat([4, 9, 16], 1/2), '', 0.0); + self::assertEquals([2.0, 3.0, 4.0], Functions::powerFloat([8, 27, 64], 1/3), '', 0.0); } } From 9382c30d35f39dd5de6534478d1c739facb0721b Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 23 Feb 2018 18:05:38 +0100 Subject: [PATCH 13/13] Add gitattributes --- .gitattributes | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..4bcedf401 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,33 @@ +* text=false + +# Force the following filetypes to have unix eols, so Windows does not break them +*.php text eol=lf +*.js text eol=lf +*.sh text eol=lf +*.css text eol=lf +*.md text eol=lf +*.txt text eol=lf +*.htaccess text eol=lf +*.json text eol=lf + +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.mov binary +*.mp4 binary +*.mp3 binary +*.flv binary +*.fla binary +*.swf binary +*.gz binary +*.zip binary +*.7z binary +*.ttf binary +*.eot binary +*.woff binary +*.pyc binary +*.pdf binary +*.dat binary +*.z binary \ No newline at end of file