From 8fef7eeb30207cde427649e251962a993ab5c898 Mon Sep 17 00:00:00 2001 From: Alexey Kopytko Date: Sat, 16 Mar 2024 10:16:42 +0900 Subject: [PATCH 1/2] Extend RunningVariance with min and max --- src/Helper/RunningVariance.php | 39 ++++++++++++++++++++++++++++ tests/Helper/RunningVarianceTest.php | 32 +++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/Helper/RunningVariance.php b/src/Helper/RunningVariance.php index fde558e..d998574 100644 --- a/src/Helper/RunningVariance.php +++ b/src/Helper/RunningVariance.php @@ -39,6 +39,12 @@ class RunningVariance */ private int $count = 0; + /** The smallest observed value */ + private float $min = NAN; + + /** The largest observed value */ + private float $max = NAN; + /** First moment: the mean value. */ private float $mean = 0.0; @@ -61,6 +67,19 @@ public function observe(float $value): float $this->mean += $delta / $this->count; $this->m2 += $delta * ($value - $this->mean); + if (1 === $this->count) { + $this->min = $value; + $this->max = $value; + } else { + if ($value < $this->min) { + $this->min = $value; + } + + if ($value > $this->max) { + $this->max = $value; + } + } + return $value; } @@ -72,6 +91,16 @@ public function getCount(): int return $this->count; } + public function getMin(): float + { + return $this->min; + } + + public function getMax(): float + { + return $this->max; + } + /** * Get the mean value. */ @@ -129,6 +158,8 @@ private function merge(self $other): void $this->count = $other->count; $this->mean = $other->mean; $this->m2 = $other->m2; + $this->min = $other->min; + $this->max = $other->max; return; } @@ -139,5 +170,13 @@ private function merge(self $other): void $this->mean = ($this->count * $this->mean) / $count + ($other->count * $other->mean) / $count; $this->m2 = $this->m2 + $other->m2 + ($delta ** 2 * $this->count * $other->count / $count); $this->count = $count; + + if ($other->min < $this->min) { + $this->min = $other->min; + } + + if ($other->max > $this->max) { + $this->max = $other->max; + } } } diff --git a/tests/Helper/RunningVarianceTest.php b/tests/Helper/RunningVarianceTest.php index dd6d5e2..5d802a9 100644 --- a/tests/Helper/RunningVarianceTest.php +++ b/tests/Helper/RunningVarianceTest.php @@ -51,6 +51,8 @@ public function testEmpty(): void $this->assertSame(0, $variance->getCount()); $this->assertNan($variance->getMean()); + $this->assertNan($variance->getMin()); + $this->assertNan($variance->getMax()); $this->assertNan($variance->getVariance()); $this->assertNan($variance->getStandardDeviation()); } @@ -72,10 +74,26 @@ public function testOne(): void $this->assertSame(1, $variance->getCount()); $this->assertSame(M_PI, $variance->getMean()); + $this->assertSame(M_PI, $variance->getMin()); + $this->assertSame(M_PI, $variance->getMax()); $this->assertSame(0.0, $variance->getVariance()); $this->assertSame(0.0, $variance->getStandardDeviation()); } + public function testOneNegative(): void + { + $variance = new RunningVariance(); + $variance->observe(-1.01); + + $this->assertSame(1, $variance->getCount()); + $this->assertSame(-1.01, $variance->getMean()); + $this->assertSame(-1.01, $variance->getMin()); + $this->assertSame(-1.01, $variance->getMax()); + $this->assertSame(0.0, $variance->getVariance()); + $this->assertSame(0.0, $variance->getStandardDeviation()); + } + + public function testTwo(): void { $variance = new RunningVariance(); @@ -84,6 +102,8 @@ public function testTwo(): void $this->assertSame(2, $variance->getCount()); $this->assertSame(M_PI, $variance->getMean()); + $this->assertSame(M_PI, $variance->getMin()); + $this->assertSame(M_PI, $variance->getMax()); $this->assertSame(0.0, $variance->getVariance()); $this->assertSame(0.0, $variance->getStandardDeviation()); } @@ -96,6 +116,8 @@ public function testNAN(): void $this->assertSame(2, $variance->getCount()); $this->assertNan($variance->getMean()); + $this->assertSame(M_PI, $variance->getMin()); + $this->assertSame(M_PI, $variance->getMax()); $this->assertNan($variance->getVariance()); $this->assertNan($variance->getStandardDeviation()); } @@ -111,6 +133,8 @@ public function testFive(): void $this->assertSame(5, $variance->getCount()); $this->assertSame(5.0, $variance->getMean()); + $this->assertSame(2.0, $variance->getMin()); + $this->assertSame(8.0, $variance->getMax()); $this->assertEqualsWithDelta(5.0, $variance->getVariance(), 0.0001); $this->assertEqualsWithDelta(sqrt(5.0), $variance->getStandardDeviation(), 0.0001); } @@ -128,6 +152,8 @@ public function testCopy(): void $this->assertSame(5, $variance->getCount()); $this->assertSame(5.0, $variance->getMean()); + $this->assertSame(2.0, $variance->getMin()); + $this->assertSame(8.0, $variance->getMax()); $this->assertEqualsWithDelta(5.0, $variance->getVariance(), 0.0001); $this->assertEqualsWithDelta(sqrt(5.0), $variance->getStandardDeviation(), 0.0001); } @@ -145,6 +171,8 @@ public function testFiveMerged(): void $this->assertSame(5, $variance->getCount()); $this->assertSame(5.0, $variance->getMean()); + $this->assertSame(2.0, $variance->getMin()); + $this->assertSame(8.0, $variance->getMax()); $this->assertEqualsWithDelta(5.0, $variance->getVariance(), 0.0001); $this->assertEqualsWithDelta(sqrt(5.0), $variance->getStandardDeviation(), 0.0001); } @@ -164,6 +192,8 @@ public function testFiveMergedTwice(): void $this->assertSame(5, $variance->getCount()); $this->assertSame(5.0, $variance->getMean()); + $this->assertSame(2.0, $variance->getMin()); + $this->assertSame(8.0, $variance->getMax()); $this->assertEqualsWithDelta(5.0, $variance->getVariance(), 0.0001); $this->assertEqualsWithDelta(sqrt(5.0), $variance->getStandardDeviation(), 0.0001); } @@ -185,6 +215,8 @@ public function testFiveMergedThrice(): void $this->assertSame(5, $variance->getCount()); $this->assertSame(5.0, $variance->getMean()); + $this->assertSame(2.0, $variance->getMin()); + $this->assertSame(8.0, $variance->getMax()); $this->assertEqualsWithDelta(5.0, $variance->getVariance(), 0.0001); $this->assertEqualsWithDelta(sqrt(5.0), $variance->getStandardDeviation(), 0.0001); } From e3c1efb028c72a244c8c8eed32fe711ba782fdfa Mon Sep 17 00:00:00 2001 From: Alexey Kopytko Date: Sat, 16 Mar 2024 10:21:13 +0900 Subject: [PATCH 2/2] Comments --- src/Helper/RunningVariance.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Helper/RunningVariance.php b/src/Helper/RunningVariance.php index d998574..6d0d86f 100644 --- a/src/Helper/RunningVariance.php +++ b/src/Helper/RunningVariance.php @@ -91,11 +91,13 @@ public function getCount(): int return $this->count; } + /** The smallest observed value */ public function getMin(): float { return $this->min; } + /** The largest observed value */ public function getMax(): float { return $this->max;