diff --git a/Math/Statistic/Average.php b/Math/Statistic/Average.php index d510a88d0..d3b9d3cbf 100644 --- a/Math/Statistic/Average.php +++ b/Math/Statistic/Average.php @@ -157,7 +157,7 @@ class Average * * @return float * - * @throws \Exception + * @throws phpOMS\Math\Exception\ZeroDevisionException * * @since 1.0.0 */ @@ -309,52 +309,6 @@ class Average return rad2deg(atan2($y, $x)); } - /** - * Calculate angle based on time. - * - * Example: ('08:44:28') - * - * @param string $time Time - * - * @return float - * - * @throws \Exception - * - * @since 1.0.0 - */ - public static function timeToAngle(string $time) : float - { - $parts = explode(':', $time); - - if (count($parts) !== 3) { - throw new \Exception('Wrong time format'); - } - - $sec = ($parts[0] * 3600) + ($parts[1] * 60) + $parts[2]; - $angle = 360.0 * ($sec / 86400.0); - - return $angle; - } - - /** - * Calculate time based on angle. - * - * Example: ('08:44:28') - * - * @param float $angle Angle - * - * @return string - * - * @since 1.0.0 - */ - public static function angleToTime(float $angle) : string - { - $sec = 86400.0 * $angle / 360.0; - $time = sprintf('%02d:%02d:%02d', floor($sec / 3600), floor(($sec % 3600) / 60), $sec % 60); - - return $time; - } - /** * Calculate the angle mean. * diff --git a/Math/Statistic/Basic.php b/Math/Statistic/Basic.php index 9b27479d1..c1905a96c 100644 --- a/Math/Statistic/Basic.php +++ b/Math/Statistic/Basic.php @@ -36,7 +36,7 @@ class Basic * * @since 1.0.0 */ - public static function freaquency(array $values) : array + public static function frequency(array $values) : array { $freaquency = []; $sum = 1; @@ -46,8 +46,8 @@ class Basic } foreach ($values as $value) { - if ($isArray) { - $freaquency[] = self::freaquency($value); + if (is_array($value)) { + $freaquency[] = self::frequency($value); } else { $freaquency[] = $value / $sum; } diff --git a/Math/Statistic/Correlation.php b/Math/Statistic/Correlation.php index d4472f172..578356336 100644 --- a/Math/Statistic/Correlation.php +++ b/Math/Statistic/Correlation.php @@ -59,11 +59,11 @@ class Correlation $count = count($x); $sum = 0.0; - for ($i = $k + 1; $i < $count; ++$i) { + for ($i = $k; $i < $count; ++$i) { $sum += ($x[$i] - $mean) * ($x[$i - $k] - $mean); } - return $sum / ($squaredMeanDeviation * count($x)); + return $sum / ($squaredMeanDeviation * $count); } /** @@ -71,40 +71,41 @@ class Correlation * * @param array $autocorrelations Autocorrelations * @param int $h Maximum leg considered + * @param int $n Amount of observations * * @return float * * @since 1.0.0 */ - public static function boxPierceTest(array $autocorrelations, int $h) : float + public static function boxPierceTest(array $autocorrelations, int $h, int $n) : float { $sum = 0; for ($i = 0; $i < $h; ++$i) { $sum += $autocorrelations[$i] ** 2; } - return count($autocorrelations) * $sum; + return $n * $sum; } /** - * Box Pierce test (portmanteau test). + * Ljung Box test (portmanteau test). * * @param array $autocorrelations Autocorrelations * @param int $h Maximum leg considered + * @param int $n Amount of observations * * @return float * * @since 1.0.0 */ - public static function ljungBoxTest(array $autocorrelations, int $h) : float + public static function ljungBoxTest(array $autocorrelations, int $h, int $n) : float { - $count = count($autocorrelations); - $sum = 0; + $sum = 0; for ($i = 0; $i < $h; ++$i) { - $sum += 1 / ($count - $i) * $autocorrelations[$i] ** 2; + $sum += 1 / ($n - ($i + 1)) * $autocorrelations[$i] ** 2; } - return $count * ($count + 2) * $sum; + return $n * ($n + 2) * $sum; } } diff --git a/Math/Statistic/MeasureOfDispersion.php b/Math/Statistic/MeasureOfDispersion.php index 8b0caf2a3..ea7e5b7ac 100644 --- a/Math/Statistic/MeasureOfDispersion.php +++ b/Math/Statistic/MeasureOfDispersion.php @@ -85,7 +85,14 @@ class MeasureOfDispersion */ public static function standardDeviation(array $values) : float { - return sqrt(self::sampleVariance($values)); + $mean = Average::arithmeticMean($values); + $sum = 0.0; + + foreach ($values as $value) { + $sum += ($value - $mean) ** 2; + } + + return sqrt($sum / (count($values) - 1)); } /** @@ -109,7 +116,7 @@ class MeasureOfDispersion throw new ZeroDevisionException(); } - return $count * self::empiricalVariance($values) / ($count - 1); + return self::empiricalVariance($values) * $count / ($count - 1); } /** @@ -117,7 +124,8 @@ class MeasureOfDispersion * * Example: ([4, 5, 9, 1, 3]) * - * @param array $values Values + * @param array $values Values + * @param array $probabilities Probabilities * * @return float * @@ -125,22 +133,23 @@ class MeasureOfDispersion * * @since 1.0.0 */ - public static function empiricalVariance(array $values) : float + public static function empiricalVariance(array $values, array $probabilities = []) : float { - $count = count($values); + $count = count($values); + $hasProbability = !empty($probabilities); if ($count === 0) { throw new ZeroDevisionException(); } - $mean = Average::arithmeticMean($values); + $mean = $hasProbability ? Average::weightedAverage($values, $probabilities) : Average::arithmeticMean($values); $sum = 0; - foreach ($values as $value) { - $sum += $value - $mean; + foreach ($values as $key => $value) { + $sum += ($hasProbability ? $probabilities[$key] : 1) * ($value - $mean) ** 2; } - return $sum / ($count - 1); + return $hasProbability ? $sum : $sum / $count; } /** @@ -216,6 +225,27 @@ class MeasureOfDispersion return $sum / count($x); } + /** + * Get mean absolute deviation. + * + * @param array $x Values + * + * @return float + * + * @since 1.0.0 + */ + public static function meanAbsoluteDeviation(array $x) : float + { + $mean = Average::arithmeticMean($x); + $sum = 0.0; + + foreach ($x as $xi) { + $sum += abs($xi - $mean); + } + + return $sum / count($x); + } + /** * Get squared mean deviation. * diff --git a/tests/Math/Statistic/AverageTest.php b/tests/Math/Statistic/AverageTest.php index 0468cf9fe..dc837ef4e 100644 --- a/tests/Math/Statistic/AverageTest.php +++ b/tests/Math/Statistic/AverageTest.php @@ -21,4 +21,66 @@ class AverageTest extends \PHPUnit\Framework\TestCase { self::assertEquals(-3 / 2, Average::averageDatasetChange([6, 7, 6, 3, 0])); } + + public function testMean() + { + self::assertEquals(4, Average::arithmeticMean([1, 2, 3, 4, 5, 6, 7]), '', 0.01); + + self::assertEquals(69 / 20, Average::weightedAverage( + [1, 2, 3, 4, 5, 6, 7], + [0.1, 0.2, 0.3, 0.1, 0.2, 0.05, 0.05] + ), '', 0.01); + + self::assertEquals(3.3800151591413, Average::geometricMean([1, 2, 3, 4, 5, 6, 7]), '', 0.01); + self::assertEquals(2.6997245179063, Average::harmonicMean([1, 2, 3, 4, 5, 6, 7]), '', 0.01); + + self::assertEquals(-90, Average::angleMean([90.0, 180.0, 270.0, 360.0]), '', 0.01); + self::assertEquals(9.999999999999977, Average::angleMean([370.0]), '', 0.01); + + self::assertEquals(270, Average::angleMean2([90.0, 180.0, 270.0, 360.0]), '', 0.01); + self::assertEquals(9.999999999999977, Average::angleMean2([370.0]), '', 0.01); + } + + /** + * @expectedException phpOMS\Math\Matrix\Exception\InvalidDimensionException + */ + public function testInvalidWeightedAverageDimension() + { + Average::weightedAverage([1, 2, 3, 4, 5, 6, 7], [0.1, 0.2, 0.3, 0.1, 0.2, 0.05]); + } + + /** + * @expectedException phpOMS\Math\Exception\ZeroDevisionException + */ + public function testInvalidArithmeticMeanZeroDevision() + { + Average::arithmeticMean([]); + } + + /** + * @expectedException phpOMS\Math\Exception\ZeroDevisionException + */ + public function testInvalidGeometricMean() + { + Average::geometricMean([]); + } + + /** + * @expectedException phpOMS\Math\Exception\ZeroDevisionException + */ + public function testInvalidHarmonicMean() + { + Average::harmonicMean([1, 2, 3, 0, 5, 6, 7]); + } + + public function testMode() + { + self::assertEquals(2, Average::mode([1, 2, 2, 3, 4, 4, 2]), '', 0.01); + } + + public function testMedia() + { + self::assertEquals(4, Average::median([1, 2, 3, 4, 5, 6, 7]), '', 0.01); + self::assertEquals(3.5, Average::median([1, 2, 3, 4, 5, 6]), '', 0.01); + } } diff --git a/tests/Math/Statistic/BasicTest.php b/tests/Math/Statistic/BasicTest.php index c6fb80a45..ff54c8e62 100644 --- a/tests/Math/Statistic/BasicTest.php +++ b/tests/Math/Statistic/BasicTest.php @@ -17,8 +17,16 @@ use phpOMS\Math\Statistic\Basic; class BasicTest extends \PHPUnit\Framework\TestCase { - public function testPlaceholder() + public function testFrequency() { - self::markTestIncomplete(); + self::assertEquals( + [1 / 10, 2 / 10, 3 / 10, 4 / 10], + Basic::frequency([1, 2, 3, 4]) + ); + + self::assertEquals( + [1 / 10, 2 / 10, 3 / 10, [1 / 6, 2 / 6, 3 / 6], 4 / 10], + Basic::frequency([1, 2, 3, [1, 2, 3], 4]) + ); } } diff --git a/tests/Math/Statistic/CorrelationTest.php b/tests/Math/Statistic/CorrelationTest.php index d2a8be202..f058911eb 100644 --- a/tests/Math/Statistic/CorrelationTest.php +++ b/tests/Math/Statistic/CorrelationTest.php @@ -17,8 +17,45 @@ use phpOMS\Math\Statistic\Correlation; class CorrelationTest extends \PHPUnit\Framework\TestCase { - public function testPlaceholder() + public function testBravisPersonCorrelationCoefficient() { - self::markTestIncomplete(); + self::assertEquals( + 0.8854, + Correlation::bravaisPersonCorrelationCoefficient( + [1, 2, 3, 4, 5, 6, 7], + [3, 4, 5, 9, 7, 8, 9] + ), '', 0.01 + ); + } + + public function testAutocorrelationCoefficient() + { + $data = [ + 1, 20, 31, 8, 40, 41, 46, 89, 72, 45, 81, 93, + 41, 63, 17, 96, 68, 27, 41, 17, 26, 75, 63, 93, + 18, 93, 80, 36, 4, 23, 81, 47, 61, 27, 13, 25, + 51, 20, 65, 45, 87, 68, 36, 31, 79, 7, 95, 37 + ]; + + self::assertEquals(0.022, Correlation::autocorrelationCoefficient($data, 1), '', 0.01); + self::assertEquals(0.098, Correlation::autocorrelationCoefficient($data, 2), '', 0.01); + } + + public function testPortmanteauTest() + { + $data = [ + 1, 20, 31, 8, 40, 41, 46, 89, 72, 45, 81, 93, + 41, 63, 17, 96, 68, 27, 41, 17, 26, 75, 63, 93, + 18, 93, 80, 36, 4, 23, 81, 47, 61, 27, 13, 25, + 51, 20, 65, 45, 87, 68, 36, 31, 79, 7, 95, 37 + ]; + + $correlations = []; + for ($i = 0; $i < 24; $i++) { + $correlations[] = Correlation::autocorrelationCoefficient($data, $i + 1); + } + + self::assertEquals(16.46, Correlation::boxPierceTest($correlations, 24, 48), '', 0.01); + self::assertEquals(24.92, Correlation::ljungBoxTest($correlations, 24, 48), '', 0.01); } } diff --git a/tests/Math/Statistic/MeasureOfDispersionTest.php b/tests/Math/Statistic/MeasureOfDispersionTest.php index 2e8b012ec..2308f1a0e 100644 --- a/tests/Math/Statistic/MeasureOfDispersionTest.php +++ b/tests/Math/Statistic/MeasureOfDispersionTest.php @@ -21,4 +21,70 @@ class MeasureOfDispersionTest extends \PHPUnit\Framework\TestCase { self::assertEquals((float) (9 - 1), MeasureOfDispersion::range([4, 5, 9, 1, 3])); } + + public function testStandardDeviation() + { + self::assertEquals(2.160246, MeasureOfDispersion::standardDeviation([1, 2, 3, 4, 5, 6, 7]), '', 0.01); + } + + public function testEmpiricalCovariance() + { + self::assertEquals( + 4.667, + MeasureOfDispersion::empiricalCovariance( + [1, 2, 3, 4, 5, 6, 7], + [3, 4, 5, 9, 7, 8, 9] + ), '', 0.01 + ); + } + + public function testVariance() + { + self::assertEquals(6219.9, MeasureOfDispersion::sampleVariance([3, 21, 98, 203, 17, 9]), '', 0.01); + self::assertEquals(5183.25, MeasureOfDispersion::empiricalVariance([3, 21, 98, 203, 17, 9]), '', 0.01); + } + + public function testDeviation() + { + self::assertEquals(0.0, MeasureOfDispersion::meanDeviation([3, 4, 5, 9, 7, 8, 9]), '', 0.01); + self::assertEquals(2.0816, MeasureOfDispersion::meanAbsoluteDeviation([3, 4, 5, 9, 7, 8, 9]), '', 0.01); + self::assertEquals((12.96 + 2.56 + 0.36 + 5.76 + 11.56) / 5, MeasureOfDispersion::squaredMeanDeviation([1, 3, 4, 7, 8]), '', 0.01); + } + + public function testEmpiricalVariationCoefficient() + { + self::assertEquals(0.5400, MeasureOfDispersion::empiricalVariationCoefficient([1, 2, 3, 4, 5, 6, 7]), '', 0.01); + } + + /** + * @expectedException phpOMS\Math\Exception\ZeroDevisionException + */ + public function testInvalidEmpiricalCovariance() + { + MeasureOfDispersion::empiricalCovariance([], []); + } + + /** + * @expectedException phpOMS\Math\Matrix\Exception\InvalidDimensionException + */ + public function testInvalidEmpiricalCovarianceDimension() + { + MeasureOfDispersion::empiricalCovariance([1, 2, 3, 4], [1, 2, 3]); + } + + /** + * @expectedException phpOMS\Math\Exception\ZeroDevisionException + */ + public function testInvalidSampleVariance() + { + MeasureOfDispersion::sampleVariance([]); + } + + /** + * @expectedException phpOMS\Math\Exception\ZeroDevisionException + */ + public function testInvalidEmpiricalVariance() + { + MeasureOfDispersion::empiricalVariance([]); + } }