From 0e8876ce4d937483f0403be56ee582b69f1ad1f9 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 2 Sep 2018 21:26:00 +0200 Subject: [PATCH] More unit tests --- DataStorage/Cookie/CookieJar.php | 2 +- Math/Statistic/Average.php | 26 +++++++-------- Math/Statistic/Forecast/Error.php | 4 +-- .../Regression/MultipleLinearRegression.php | 16 ++++++++++ .../Regression/RegressionAbstract.php | 10 ++---- Math/Statistic/MeasureOfDispersion.php | 15 ++++++++- .../Database/DataMapperAbstractTest.php | 7 ++++ tests/Math/Statistic/Forecast/ErrorTest.php | 28 ++++++++++++++++ .../Regression/LevelLevelRegressionTest.php | 32 +++++++++++++++++++ .../Regression/LevelLogRegressionTest.php | 31 ++++++++++++++++++ .../Regression/LogLevelRegressionTest.php | 30 +++++++++++++++++ .../Regression/LogLogRegressionTest.php | 30 +++++++++++++++++ .../Statistic/MeasureOfDispersionTest.php | 6 ++++ 13 files changed, 212 insertions(+), 25 deletions(-) create mode 100644 tests/Math/Statistic/Forecast/Regression/LevelLevelRegressionTest.php create mode 100644 tests/Math/Statistic/Forecast/Regression/LevelLogRegressionTest.php create mode 100644 tests/Math/Statistic/Forecast/Regression/LogLevelRegressionTest.php create mode 100644 tests/Math/Statistic/Forecast/Regression/LogLogRegressionTest.php diff --git a/DataStorage/Cookie/CookieJar.php b/DataStorage/Cookie/CookieJar.php index 65b1be74a..948e7219e 100644 --- a/DataStorage/Cookie/CookieJar.php +++ b/DataStorage/Cookie/CookieJar.php @@ -129,7 +129,7 @@ final class CookieJar throw new LockException('CookieJar'); } - if (!headers_sent()) { + if (!\headers_sent()) { \setcookie($id, '', time() - 3600); return true; diff --git a/Math/Statistic/Average.php b/Math/Statistic/Average.php index 85f496981..63f43c7ac 100644 --- a/Math/Statistic/Average.php +++ b/Math/Statistic/Average.php @@ -124,9 +124,9 @@ final class Average $start = $t - 1 - ($periods - 2); if (!empty($weight)) { - return self::weightedAverage(array_slice($x, $start, $end - $start), array_slice($weight, $start, $end - $start)); + return self::weightedAverage(\array_slice($x, $start, $end - $start), \array_slice($weight, $start, $end - $start)); } else { - return self::arithmeticMean(array_slice($x, $start, $end - $start)); + return self::arithmeticMean(\array_slice($x, $start, $end - $start)); } } @@ -180,7 +180,7 @@ final class Average throw new ZeroDevisionException(); } - return array_sum($values) / $count; + return \array_sum($values) / $count; } /** @@ -196,7 +196,7 @@ final class Average */ public static function mode(array $values) : float { - $count = array_count_values($values); + $count = \array_count_values($values); $best = \max($count); return (float) (\array_keys($count, $best)[0] ?? 0.0); @@ -217,7 +217,7 @@ final class Average { \sort($values); $count = \count($values); - $middleval = (int) floor(($count - 1) / 2); + $middleval = (int) \floor(($count - 1) / 2); if ($count % 2) { $median = $values[$middleval]; @@ -274,7 +274,7 @@ final class Average \sort($values); if ($offset > 0) { - $values = array_slice($values, $offset, -$offset); + $values = \array_slice($values, $offset, -$offset); } $count = \count($values); @@ -310,14 +310,14 @@ final class Average $size = \count($angles); for ($i = 0; $i < $size; ++$i) { - $x += \cos(deg2rad($angles[$i])); - $y += \sin(deg2rad($angles[$i])); + $x += \cos(\deg2rad($angles[$i])); + $y += \sin(\deg2rad($angles[$i])); } $x /= $size; $y /= $size; - return rad2deg(atan2($y, $x)); + return \rad2deg(\atan2($y, $x)); } /** @@ -337,20 +337,20 @@ final class Average \sort($angles); if ($offset > 0) { - $angles = array_slice($angles, $offset, -$offset); + $angles = \array_slice($angles, $offset, -$offset); } $sins = 0.0; $coss = 0.0; foreach ($angles as $a) { - $sins += \sin(deg2rad($a)); - $coss += \cos(deg2rad($a)); + $sins += \sin(\deg2rad($a)); + $coss += \cos(\deg2rad($a)); } $avgsin = $sins / (0.0 + \count($angles)); $avgcos = $coss / (0.0 + \count($angles)); - $avgang = rad2deg(atan2($avgsin, $avgcos)); + $avgang = \rad2deg(\atan2($avgsin, $avgcos)); while ($avgang < 0.0) { $avgang += 360.0; diff --git a/Math/Statistic/Forecast/Error.php b/Math/Statistic/Forecast/Error.php index 5dc05ca1c..7f76260b5 100644 --- a/Math/Statistic/Forecast/Error.php +++ b/Math/Statistic/Forecast/Error.php @@ -356,7 +356,7 @@ class Error } /** - * Get mean absolute scaled error (MASE) + * Get mean absolute scaled error (MSSE) * * @param array $scaledErrors Scaled errors * @@ -386,7 +386,7 @@ class Error $naive = 1 / (\count($observed) - $m) * self::getNaiveForecast($observed, $m); foreach ($errors as $error) { - $error[] = $error / $naive; + $scaled[] = $error / $naive; } return $scaled; diff --git a/Math/Statistic/Forecast/Regression/MultipleLinearRegression.php b/Math/Statistic/Forecast/Regression/MultipleLinearRegression.php index f55c33154..8ed5a374e 100644 --- a/Math/Statistic/Forecast/Regression/MultipleLinearRegression.php +++ b/Math/Statistic/Forecast/Regression/MultipleLinearRegression.php @@ -45,4 +45,20 @@ class MultipleLinearRegression public static function getPredictionInterval() : array { } + + /** + * {@inheritdoc} + */ + public static function getSlope(float $b1, float $y, float $x) : float + { + return 0.0; + } + + /** + * {@inheritdoc} + */ + public static function getElasticity(float $b1, float $y, float $x): float + { + return 0.0; + } } diff --git a/Math/Statistic/Forecast/Regression/RegressionAbstract.php b/Math/Statistic/Forecast/Regression/RegressionAbstract.php index d50ed7e22..163a30ad2 100644 --- a/Math/Statistic/Forecast/Regression/RegressionAbstract.php +++ b/Math/Statistic/Forecast/Regression/RegressionAbstract.php @@ -164,10 +164,7 @@ abstract class RegressionAbstract * * @since 1.0.0 */ - public static function getSlope(float $b1, float $x, float $y) : float - { - return 0.0; - } + abstract public static function getSlope(float $b1, float $x, float $y) : float; /** * Get elasticity @@ -180,8 +177,5 @@ abstract class RegressionAbstract * * @since 1.0.0 */ - public static function getElasticity(float $b1, float $x, float $y): float - { - return 0.0; - } + abstract public static function getElasticity(float $b1, float $x, float $y): float; } diff --git a/Math/Statistic/MeasureOfDispersion.php b/Math/Statistic/MeasureOfDispersion.php index ec2e86f17..d5151526c 100644 --- a/Math/Statistic/MeasureOfDispersion.php +++ b/Math/Statistic/MeasureOfDispersion.php @@ -229,7 +229,20 @@ final class MeasureOfDispersion */ public static function getIQR(array $x) : float { - return 0.0; + $count = \count($x); + + if ($count % 2 !== 0) { + --$count; + } + + $count /= 2; + + \sort($x); + + $Q1 = Average::median(\array_slice($x, 0, $count)); + $Q3 = Average::median(\array_slice($x, -$count, $count)); + + return $Q3 - $Q1; } /** diff --git a/tests/DataStorage/Database/DataMapperAbstractTest.php b/tests/DataStorage/Database/DataMapperAbstractTest.php index 375783e5a..26a911651 100644 --- a/tests/DataStorage/Database/DataMapperAbstractTest.php +++ b/tests/DataStorage/Database/DataMapperAbstractTest.php @@ -146,6 +146,13 @@ class DataMapperAbstractTest extends \PHPUnit\Framework\TestCase $GLOBALS['dbpool']->get()->con->prepare('DROP TABLE oms_test_has_many_rel_relations')->execute(); } + public function testDefault() + { + self::assertEquals('test_base_id', BaseModelMapper::getPrimaryField()); + self::assertEquals('test_base', BaseModelMapper::getTable()); + self::assertEquals('test_base_datetime', BaseModelMapper::getCreatedAt()); + } + public function testCreate() { self::assertGreaterThan(0, BaseModelMapper::create($this->model)); diff --git a/tests/Math/Statistic/Forecast/ErrorTest.php b/tests/Math/Statistic/Forecast/ErrorTest.php index 2718fbe8f..2a0003309 100644 --- a/tests/Math/Statistic/Forecast/ErrorTest.php +++ b/tests/Math/Statistic/Forecast/ErrorTest.php @@ -32,6 +32,8 @@ class ErrorTest extends \PHPUnit\Framework\TestCase [300, 700, 200, -300] ) ); + + self::assertEquals([Error::getForecastError(1000, 700)], Error::getForecastErrorArray([1000], [700])); } public function testErrorPercentage() @@ -67,4 +69,30 @@ class ErrorTest extends \PHPUnit\Framework\TestCase self::assertEquals(125000, Error::getMeanSquaredError($errors), '', 0.01); self::assertEquals(406.2019, Error::getRootMeanSquaredError($errors), '', 0.01); } + + public function testMASE() + { + $observed = [ + -2.9, -2.83, -0.95, -0.88, 1.21, -1.67, 0.83, -0.27, 1.36, + -0.34, 0.48, -2.83, -0.95, -0.88, 1.21, -1.67, -2.99, 1.24, 0.64 + ]; + + $forecast = [ + -2.95, -2.7, -1.00, -0.68, 1.50, -1.00, 0.90, -0.37, 1.26, + -0.54, 0.58, -2.13, -0.75, -0.89, 1.25, -1.65, -3.20, 1.29, 0.60 + ]; + + $errors = Error::getForecastErrorArray($observed, $forecast); + $scaledErrors = Error::getScaledErrorArray($errors, $observed); + + self::assertEquals(0.0983, Error::getMeanAbsoluteScaledError($scaledErrors), '', 0.01); + } + + public function testScaledError() + { + self::assertEquals( + [Error::getScaledError(Error::getForecastError(1000, 700), 1000)], + Error::getScaledErrorArray([Error::getForecastError(1000, 700)], [1000]) + ); + } } diff --git a/tests/Math/Statistic/Forecast/Regression/LevelLevelRegressionTest.php b/tests/Math/Statistic/Forecast/Regression/LevelLevelRegressionTest.php new file mode 100644 index 000000000..67f84606b --- /dev/null +++ b/tests/Math/Statistic/Forecast/Regression/LevelLevelRegressionTest.php @@ -0,0 +1,32 @@ + 3, 'b1' => 4], $reg, '', 0.2); + self::assertEquals(4, LevelLevelRegression::getSlope($reg['b1'], 0, 0)); + self::assertEquals(22, LevelLevelRegression::getElasticity($reg['b1'], 11, 2)); + } +} diff --git a/tests/Math/Statistic/Forecast/Regression/LevelLogRegressionTest.php b/tests/Math/Statistic/Forecast/Regression/LevelLogRegressionTest.php new file mode 100644 index 000000000..d5926e8bc --- /dev/null +++ b/tests/Math/Statistic/Forecast/Regression/LevelLogRegressionTest.php @@ -0,0 +1,31 @@ + 1, 'b1' => 1], $reg, '', 0.2); + } +} diff --git a/tests/Math/Statistic/Forecast/Regression/LogLevelRegressionTest.php b/tests/Math/Statistic/Forecast/Regression/LogLevelRegressionTest.php new file mode 100644 index 000000000..6850dd546 --- /dev/null +++ b/tests/Math/Statistic/Forecast/Regression/LogLevelRegressionTest.php @@ -0,0 +1,30 @@ + y = e^(-1 + 2 * x) + $x = [0.25, 0.5, 1, 1.5]; + $y = [0.6065, 1, 2.718, 7.389]; + + $reg = LogLevelRegression::getRegression($x, $y); + + self::assertEquals(['b0' => -1, 'b1' => 2], $reg, '', 0.2); + } +} diff --git a/tests/Math/Statistic/Forecast/Regression/LogLogRegressionTest.php b/tests/Math/Statistic/Forecast/Regression/LogLogRegressionTest.php new file mode 100644 index 000000000..d0ef28516 --- /dev/null +++ b/tests/Math/Statistic/Forecast/Regression/LogLogRegressionTest.php @@ -0,0 +1,30 @@ + y = e^(2 + 3 * ln(x)) + $x = [0.25, 0.5, 1, 1.5]; + $y = [0.115, 0.924, 7.389, 24.938]; + + $reg = LogLogRegression::getRegression($x, $y); + + self::assertEquals(['b0' => 2, 'b1' => 3], $reg, '', 0.2); + } +} diff --git a/tests/Math/Statistic/MeasureOfDispersionTest.php b/tests/Math/Statistic/MeasureOfDispersionTest.php index 5c2cd03fe..0cd19dbaf 100644 --- a/tests/Math/Statistic/MeasureOfDispersionTest.php +++ b/tests/Math/Statistic/MeasureOfDispersionTest.php @@ -56,6 +56,12 @@ class MeasureOfDispersionTest extends \PHPUnit\Framework\TestCase self::assertEquals(0.5400, MeasureOfDispersion::empiricalVariationCoefficient([1, 2, 3, 4, 5, 6, 7]), '', 0.01); } + public function testIQR() + { + $x = [7, 7, 31, 31, 47, 75, 87, 115, 116, 119, 119, 155, 177]; + self::assertEquals(88, MeasureOfDispersion::getIQR($x)); + } + /** * @expectedException phpOMS\Math\Exception\ZeroDevisionException */