diff --git a/Math/Statistic/Correlation.php b/Math/Statistic/Correlation.php index abbed73e8..8de71e38c 100644 --- a/Math/Statistic/Correlation.php +++ b/Math/Statistic/Correlation.php @@ -46,9 +46,26 @@ final class Correlation * * @since 1.0.0 */ - public static function bravaisPersonCorrelationCoefficient(array $x, array $y) : float + public static function bravaisPersonCorrelationCoefficientPopulation(array $x, array $y) : float { - return MeasureOfDispersion::empiricalCovariance($x, $y) / (MeasureOfDispersion::standardDeviationSample($x) * MeasureOfDispersion::standardDeviationSample($y)); + return MeasureOfDispersion::empiricalCovariance($x, $y) / (MeasureOfDispersion::standardDeviationPopulation($x) * MeasureOfDispersion::standardDeviationPopulation($y)); + } + + /** + * Calculage bravais person correlation coefficient. + * + * Example: ([4, 5, 9, 1, 3], [4, 5, 9, 1, 3]) + * + * @param array $x Values + * @param array $y Values + * + * @return float + * + * @since 1.0.0 + */ + public static function bravaisPersonCorrelationCoefficientSample(array $x, array $y) : float + { + return MeasureOfDispersion::sampleCovariance($x, $y) / (MeasureOfDispersion::standardDeviationSample($x) * MeasureOfDispersion::standardDeviationSample($y)); } /** diff --git a/Math/Statistic/Forecast/Error.php b/Math/Statistic/Forecast/Error.php index 0ef885653..a3768aeae 100644 --- a/Math/Statistic/Forecast/Error.php +++ b/Math/Statistic/Forecast/Error.php @@ -170,7 +170,7 @@ final class Error */ public static function getCoefficientOfDetermination(array $observed, array $forecasted) : float { - return Correlation::bravaisPersonCorrelationCoefficient($observed, $forecasted) ** 2; + return Correlation::bravaisPersonCorrelationCoefficientPopulation($observed, $forecasted) ** 2; } /** diff --git a/Math/Statistic/MeasureOfDispersion.php b/Math/Statistic/MeasureOfDispersion.php index 65b106478..f87fd4e7d 100644 --- a/Math/Statistic/MeasureOfDispersion.php +++ b/Math/Statistic/MeasureOfDispersion.php @@ -101,11 +101,14 @@ final class MeasureOfDispersion $mean = $mean !== null ? $mean : Average::arithmeticMean($values); $sum = 0.0; + $valueCount = 0; + foreach ($values as $value) { $sum += ($value - $mean) ** 2; + ++$valueCount; } - return \sqrt($sum / (\count($values) - 1)); + return \sqrt($sum / ($valueCount - 1)); } /** @@ -127,11 +130,14 @@ final class MeasureOfDispersion $mean = $mean !== null ? $mean : Average::arithmeticMean($values); $sum = 0.0; + $valueCount = 0; + foreach ($values as $value) { $sum += ($value - $mean) ** 2; + ++$valueCount; } - return \sqrt($sum / \count($values)); + return \sqrt($sum / $valueCount); } /** @@ -241,7 +247,37 @@ final class MeasureOfDispersion $sum += ($x[$i] - $xMean) * ($y[$i] - $yMean); } - return $sum / ($count - 1); + return $sum / $count; + } + + /** + * Calculage empirical covariance on a sample + * + * Example: ([4, 5, 9, 1, 3], [4, 5, 9, 1, 3]) + * + * @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 float $meanX Mean + * @param float $meanY Mean + * + * @return float + * + * @throws ZeroDivisionException This exception is thrown if the size of the x array is less than 2 + * @throws InvalidDimensionException This exception is thrown if x and y have different dimensions + * + * @since 1.0.0 + */ + public static function sampleCovariance(array $x, array $y, float $meanX = null, float $meanY = null) : float + { + $count = \count($x); + + if ($count < 2) { + throw new ZeroDivisionException(); + } + + return self::empiricalCovariance($x, $y, $meanX, $meanY) * $count / ($count - 1); } /** diff --git a/tests/Math/Statistic/CorrelationTest.php b/tests/Math/Statistic/CorrelationTest.php index 53c83db04..dc89ff9f7 100644 --- a/tests/Math/Statistic/CorrelationTest.php +++ b/tests/Math/Statistic/CorrelationTest.php @@ -27,11 +27,26 @@ class CorrelationTest extends \PHPUnit\Framework\TestCase * @testdox The correlation coefficient (Bravis Person) is calculated correctly * @group framework */ - public function testBravisPersonCorrelationCoefficient() : void + public function testBravisPersonCorrelationCoefficientPopulation() : void { self::assertEqualsWithDelta( 0.8854, - Correlation::bravaisPersonCorrelationCoefficient( + Correlation::bravaisPersonCorrelationCoefficientPopulation( + [1, 2, 3, 4, 5, 6, 7], + [3, 4, 5, 9, 7, 8, 9] + ), 0.01 + ); + } + + /** + * @testdox The correlation coefficient (Bravis Person) is calculated correctly on a sample + * @group framework + */ + public function testBravisPersonCorrelationCoefficientSample() : void + { + self::assertEqualsWithDelta( + 0.8854, + Correlation::bravaisPersonCorrelationCoefficientSample( [1, 2, 3, 4, 5, 6, 7], [3, 4, 5, 9, 7, 8, 9] ), 0.01 diff --git a/tests/Math/Statistic/MeasureOfDispersionTest.php b/tests/Math/Statistic/MeasureOfDispersionTest.php index dba958e5f..948b6e727 100644 --- a/tests/Math/Statistic/MeasureOfDispersionTest.php +++ b/tests/Math/Statistic/MeasureOfDispersionTest.php @@ -56,6 +56,21 @@ class MeasureOfDispersionTest extends \PHPUnit\Framework\TestCase ); } + /** + * @testdox The empirical covariance on a sample is correctly calculated + * @group framework + */ + public function testSampleCovariance() : void + { + self::assertEqualsWithDelta( + 4.0, + MeasureOfDispersion::sampleCovariance( + [1, 2, 3, 4, 5, 6, 7], + [3, 4, 5, 9, 7, 8, 9] + ), 0.01 + ); + } + /** * @testdox The sample variance is correctly calculated * @group framework