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 diff --git a/Math/Functions/Functions.php b/Math/Functions/Functions.php index 82e96cf0f..79ca16a8a 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 @@ -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. diff --git a/Math/Statistic/Forecast/Error.php b/Math/Statistic/Forecast/Error.php index 238cafb28..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(self::square($errors)); + return MeasureOfDispersion::squaredMeanDeviation($errors); } /** @@ -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)); } /** diff --git a/Math/Statistic/MeasureOfDispersion.php b/Math/Statistic/MeasureOfDispersion.php index 4481a6b1d..5eb1d801a 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(); @@ -80,14 +81,15 @@ class MeasureOfDispersion * @latex \sigma = \sqrt{\sigma^{2}} = \sqrt{Var(X)} * * @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) { @@ -107,6 +109,7 @@ class MeasureOfDispersion * @latex \sigma^{2} = Var(X) = \frac{1}{N - 1} \sum_{i = 1}^{N}\left(x_{i} - \bar{X}\right)^{2} * * @param array $values Values + * @param float $mean Mean * * @return float * @@ -114,7 +117,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); @@ -122,7 +125,7 @@ class MeasureOfDispersion throw new ZeroDevisionException(); } - return self::empiricalVariance($values) * $count / ($count - 1); + return self::empiricalVariance($values, [], $mean) * $count / ($count - 1); } /** @@ -136,6 +139,7 @@ class MeasureOfDispersion * * @param array $values Values * @param array $probabilities Probabilities + * @param float $mean Mean * * @return float * @@ -143,7 +147,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); @@ -152,7 +156,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) { @@ -169,8 +173,10 @@ class MeasureOfDispersion * * @latex \sigma_{XY} = cov(X, Y) = \sum_{i = 1}^{N}\frac{\left(x_{i} - \bar{X}\right) \left(y_{i} - \bar{Y}\right)}{N - 1} * - * @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 * @@ -178,7 +184,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); @@ -190,8 +196,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; @@ -219,15 +225,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) { @@ -240,15 +247,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) { @@ -261,15 +269,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) { diff --git a/tests/Math/Functions/FunctionsTest.php b/tests/Math/Functions/FunctionsTest.php index f88839b89..9fa493b10 100644 --- a/tests/Math/Functions/FunctionsTest.php +++ b/tests/Math/Functions/FunctionsTest.php @@ -68,4 +68,13 @@ 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.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); + } } diff --git a/tests/Math/Statistic/Forecast/ErrorTest.php b/tests/Math/Statistic/Forecast/ErrorTest.php index fb3d36c55..cb08f19a9 100644 --- a/tests/Math/Statistic/Forecast/ErrorTest.php +++ b/tests/Math/Statistic/Forecast/ErrorTest.php @@ -17,8 +17,54 @@ 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] + ) + ); + } + + 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); } }