Skip to content

Commit 352229b

Browse files
[ML] Fixing KDE evaluate() to return correct ValueAndMagnitude object (#128602)
Prior to this change, the evaluate method would return a dummy object for an input with bandwidth = 0. This would occur if the dataset had zero variance (see KDE constructor). This would then cause ChangePointDetector to fail to detect a spike on a dataset containing all equal numbers except for one spike.
1 parent 67468e6 commit 352229b

File tree

3 files changed

+60
-1
lines changed

3 files changed

+60
-1
lines changed

docs/changelog/128602.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 128602
2+
summary: ChangePointDetector now correctly detects when a constant function returns a different value
3+
area: Machine Learning
4+
type: bug
5+
issues:
6+
- 127517

x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/changepoint/KDE.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,13 @@ private interface KernelFunction {
3838
}
3939

4040
private ValueAndMagnitude evaluate(IntervalComplementFunction complement, KernelFunction kernel, double x) {
41-
if (bandwidth == 0.0 || orderedValues.length == 0) {
41+
if (orderedValues.length == 0) {
4242
return new ValueAndMagnitude(1.0, 0.0);
4343
}
44+
if (bandwidth == 0) {
45+
return new ValueAndMagnitude(x != orderedValues[0] ? 0.0 : 1.0, Math.abs(orderedValues[0] - x));
46+
}
47+
4448
int a = Math.min(lowerBound(orderedValues, x - 3.0 * bandwidth), orderedValues.length - 1);
4549
int b = Math.max(lowerBound(orderedValues, x + 3.0 * bandwidth), a + 1);
4650
// Account for all the values outside the interval [a, b) using the kernel complement.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.ml.aggs.changepoint;
9+
10+
import org.elasticsearch.test.ESTestCase;
11+
import org.elasticsearch.xpack.ml.aggs.MlAggsHelper;
12+
13+
import java.util.Arrays;
14+
15+
import static org.hamcrest.Matchers.instanceOf;
16+
17+
public class ChangePointDetectorTests extends ESTestCase {
18+
19+
/**
20+
* Testing the special case where values without the spike have a variance of 0.
21+
*/
22+
public void testConstantWithSpike() {
23+
double[] values = new double[25];
24+
Arrays.fill(values, 51);
25+
values[12] = 500001;
26+
27+
MlAggsHelper.DoubleBucketValues bucketValues = new MlAggsHelper.DoubleBucketValues(null, values);
28+
29+
ChangeType change = ChangePointDetector.getChangeType(bucketValues);
30+
31+
assertEquals(12, change.changePoint());
32+
assertThat(change, instanceOf(ChangeType.Spike.class));
33+
}
34+
35+
public void testRandomWithSpike() {
36+
double[] values = new double[25];
37+
for (int i = 0; i < values.length; i++) {
38+
values[i] = randomLongBetween(50, 53);
39+
}
40+
values[12] = 500001;
41+
42+
MlAggsHelper.DoubleBucketValues bucketValues = new MlAggsHelper.DoubleBucketValues(null, values);
43+
44+
ChangeType change = ChangePointDetector.getChangeType(bucketValues);
45+
46+
assertEquals(12, change.changePoint());
47+
assertThat(change, instanceOf(ChangeType.Spike.class));
48+
}
49+
}

0 commit comments

Comments
 (0)