Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose the number of observations #13

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
],
"require": {
"php": "^8.1",
"sanmai/pipeline": "^6.8",
"sanmai/pipeline": "^6.11",
"tumblr/chorus-timekeeper": "^0.1.0"
},
"require-dev": {
Expand Down
15 changes: 14 additions & 1 deletion src/AnomalyDetectionResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
*/
public const DIRECTION_DOWN = 'down';

/** @var int The number of observations */
private int $count;

/** @var float The standard deviation */
private float $std_dev;

Expand Down Expand Up @@ -75,32 +78,34 @@
/**
* Create a new anomaly detection result instance.
*
* @param int $count The number of observations
* @param float $std_dev The standard deviation
* @param float $mean The mean value
* @param float $latest The latest value
* @param float|int $sensitivity The sensitivity (see `SlidingWindowCounter::detectAnomaly()`)
*/
public function __construct(float $std_dev, float $mean, float $latest, float|int $sensitivity)
public function __construct(int $count, float $std_dev, float $mean, float $latest, float|int $sensitivity)
{
$this->count = $count;
$this->std_dev = $std_dev;
$this->mean = $mean;
$this->latest = $latest;
$this->sensitivity = $sensitivity;

$this->high = ceil($this->mean + ($sensitivity * $this->std_dev));

Check warning on line 95 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "RoundingFamily": --- Original +++ New @@ @@ $this->mean = $mean; $this->latest = $latest; $this->sensitivity = $sensitivity; - $this->high = ceil($this->mean + $sensitivity * $this->std_dev); + $this->high = round($this->mean + $sensitivity * $this->std_dev); $this->low = floor($this->mean - $sensitivity * $this->std_dev); if ($this->latest >= $this->low && $this->latest <= $this->high) { return;
$this->low = floor($this->mean - ($sensitivity * $this->std_dev));

Check warning on line 96 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "RoundingFamily": --- Original +++ New @@ @@ $this->latest = $latest; $this->sensitivity = $sensitivity; $this->high = ceil($this->mean + $sensitivity * $this->std_dev); - $this->low = floor($this->mean - $sensitivity * $this->std_dev); + $this->low = round($this->mean - $sensitivity * $this->std_dev); if ($this->latest >= $this->low && $this->latest <= $this->high) { return; }

if ($this->latest >= $this->low && $this->latest <= $this->high) {

Check warning on line 98 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "GreaterThanOrEqualTo": --- Original +++ New @@ @@ $this->sensitivity = $sensitivity; $this->high = ceil($this->mean + $sensitivity * $this->std_dev); $this->low = floor($this->mean - $sensitivity * $this->std_dev); - if ($this->latest >= $this->low && $this->latest <= $this->high) { + if ($this->latest > $this->low && $this->latest <= $this->high) { return; } if ($this->std_dev > 0.0) {
return;
}

if ($this->std_dev > 0.0) {

Check warning on line 102 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "GreaterThan": --- Original +++ New @@ @@ if ($this->latest >= $this->low && $this->latest <= $this->high) { return; } - if ($this->std_dev > 0.0) { + if ($this->std_dev >= 0.0) { $this->hops = abs($this->mean - $this->latest) / $this->std_dev; } if ($this->latest < $this->low) {
$this->hops = abs($this->mean - $this->latest) / $this->std_dev;

Check warning on line 103 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "Division": --- Original +++ New @@ @@ return; } if ($this->std_dev > 0.0) { - $this->hops = abs($this->mean - $this->latest) / $this->std_dev; + $this->hops = abs($this->mean - $this->latest) * $this->std_dev; } if ($this->latest < $this->low) { $this->direction = self::DIRECTION_DOWN;
}

if ($this->latest < $this->low) {

Check warning on line 106 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "LessThan": --- Original +++ New @@ @@ if ($this->std_dev > 0.0) { $this->hops = abs($this->mean - $this->latest) / $this->std_dev; } - if ($this->latest < $this->low) { + if ($this->latest <= $this->low) { $this->direction = self::DIRECTION_DOWN; } elseif ($this->latest > $this->high) { $this->direction = self::DIRECTION_UP;
$this->direction = self::DIRECTION_DOWN;
} elseif ($this->latest > $this->high) {

Check warning on line 108 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "GreaterThan": --- Original +++ New @@ @@ } if ($this->latest < $this->low) { $this->direction = self::DIRECTION_DOWN; - } elseif ($this->latest > $this->high) { + } elseif ($this->latest >= $this->high) { $this->direction = self::DIRECTION_UP; } }
$this->direction = self::DIRECTION_UP;
}
}
Expand All @@ -118,7 +123,7 @@
*
* @param int $precision the number of decimal digits to round to
*/
public function toArray(int $precision = 2): array

Check warning on line 126 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "DecrementInteger": --- Original +++ New @@ @@ * * @param int $precision the number of decimal digits to round to */ - public function toArray(int $precision = 2) : array + public function toArray(int $precision = 1) : array { return array_map(fn($val) => is_float($val) ? round($val, $precision) : $val, get_object_vars($this)); }
{
return array_map(
fn ($val) => is_float($val) ? round($val, $precision) : $val,
Expand All @@ -126,6 +131,14 @@
);
}

/**
* The number of observations
*/
public function getCount(): int
{
return $this->count;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would be able to see how many observations there are to make a better decision.


/**
* The standard deviation.
*/
Expand Down
1 change: 1 addition & 0 deletions src/SlidingWindowCounter.php
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ public function detectAnomaly(string $bucket_key, float|int $sensitivity = 2, ?i
$variance = $this->getHistoricVariance($bucket_key, $start_time);

return new AnomalyDetectionResult(
$variance->getCount(),
$variance->getStandardDeviation(),
$variance->getMean(),
$this->getLatestValue($bucket_key),
Expand Down
14 changes: 9 additions & 5 deletions tests/AnomalyDetectionResultTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ final class AnomalyDetectionResultTest extends TestCase
*/
public function testHappyPath(): void
{
$result = new AnomalyDetectionResult(0.999, 10.0, 11.0, 1);
$result = new AnomalyDetectionResult(10, 0.999, 10.0, 11.0, 1);

$this->assertFalse($result->isAnomaly());
$this->assertSame(AnomalyDetectionResult::DIRECTION_NONE, $result->getDirection());

$this->assertSame([
'count' => 10,
'std_dev' => 1.0,
'mean' => 10.0,
'sensitivity' => 1,
Expand All @@ -57,8 +58,9 @@ public function testHappyPath(): void
*/
public function testAllGetters(): void
{
$result = new AnomalyDetectionResult(1.0, 10.0, 11.0, 0.9);
$result = new AnomalyDetectionResult(11, 1.0, 10.0, 11.0, 0.9);

$this->assertSame(11, $result->getCount());
$this->assertSame(1.0, $result->getStandardDeviation());
$this->assertSame(10.0, $result->getMean());
$this->assertSame(0.9, $result->getSensitivity());
Expand All @@ -74,11 +76,12 @@ public function testAllGetters(): void
*/
public function testAnomalyDirectionUp(): void
{
$result = new AnomalyDetectionResult(1.0, 10.0, 12.011, 1);
$result = new AnomalyDetectionResult(22, 1.0, 10.0, 12.011, 1);

$this->assertTrue($result->isAnomaly());

$this->assertSame([
'count' => 22,
'std_dev' => 1.0,
'mean' => 10.0,
'sensitivity' => 1,
Expand All @@ -95,11 +98,12 @@ public function testAnomalyDirectionUp(): void
*/
public function testAnomalyDetectionDown(): void
{
$result = new AnomalyDetectionResult(1.0, 10.0, 1.123456, 3);
$result = new AnomalyDetectionResult(33, 1.0, 10.0, 1.123456, 3);

$this->assertTrue($result->isAnomaly());

$this->assertSame([
'count' => 33,
'std_dev' => 1.0,
'mean' => 10.0,
'sensitivity' => 3,
Expand Down Expand Up @@ -142,7 +146,7 @@ public static function providerDirections(): iterable
*/
public function testDirections(float $latest, bool $expected_is_anomaly, string $expected_direction): void
{
$result = new AnomalyDetectionResult(1.0, 10.0, $latest, 1);
$result = new AnomalyDetectionResult(100, 1.0, 10.0, $latest, 1);

$this->assertSame($expected_is_anomaly, $result->isAnomaly(), 'Unexpected anomaly result');
$this->assertSame($expected_direction, $result->getDirection(), 'Unexpected direction');
Expand Down
3 changes: 2 additions & 1 deletion tests/SlidingWindowCounterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public function testIncrementThrowsTimeInPast(): void
$window_size = 60;
$observation_period = 1000;

$counter = new SlidingWindowCounter('default', 60, $observation_period, new FakeCache(), $time_keeper);
$counter = new SlidingWindowCounter('default', $window_size, $observation_period, new FakeCache(), $time_keeper);

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessageMatches('/The time provided \(\d+\) is too far in the past \(current time: \d+, observation period: 1000\)/');
Expand Down Expand Up @@ -248,6 +248,7 @@ public function testVariance(): void
$this->assertSame(5, $counter->getHistoricVariance('test')->getCount());

$this->assertSame([
'count' => 5,
'std_dev' => 7.24,
'mean' => 6.82,
'sensitivity' => 3,
Expand Down
Loading