diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/changepoint/KDE.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/changepoint/KDE.java index 1a0342981c1d3..551d2db2e9580 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/changepoint/KDE.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/changepoint/KDE.java @@ -38,9 +38,13 @@ private interface KernelFunction { } private ValueAndMagnitude evaluate(IntervalComplementFunction complement, KernelFunction kernel, double x) { - if (bandwidth == 0.0 || orderedValues.length == 0) { + if (orderedValues.length == 0) { return new ValueAndMagnitude(1.0, 0.0); } + if (bandwidth == 0) { + return new ValueAndMagnitude(x != orderedValues[0] ? 0.0 : 1.0, Math.abs(orderedValues[0] - x)); + } + int a = Math.min(lowerBound(orderedValues, x - 3.0 * bandwidth), orderedValues.length - 1); int b = Math.max(lowerBound(orderedValues, x + 3.0 * bandwidth), a + 1); // Account for all the values outside the interval [a, b) using the kernel complement. diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/changepoint/ChangePointDetectorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/changepoint/ChangePointDetectorTests.java new file mode 100644 index 0000000000000..6fe1108a3f1df --- /dev/null +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/changepoint/ChangePointDetectorTests.java @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ml.aggs.changepoint; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.ml.aggs.MlAggsHelper; + +import java.util.Arrays; + +import static org.hamcrest.Matchers.instanceOf; + +public class ChangePointDetectorTests extends ESTestCase { + + /** + * Testing the special case where values without the spike have a variance of 0. + */ + public void testConstantWithSpike() { + double[] values = new double[25]; + Arrays.fill(values, 51); + values[12] = 500001; + + MlAggsHelper.DoubleBucketValues bucketValues = new MlAggsHelper.DoubleBucketValues(null, values); + + ChangeType change = ChangePointDetector.getChangeType(bucketValues); + + assertEquals(12, change.changePoint()); + assertThat(change, instanceOf(ChangeType.Spike.class)); + } + + public void testRandomWithSpike() { + double[] values = new double[25]; + for (int i = 0; i < values.length; i++) { + values[i] = randomLongBetween(50, 53); + } + values[12] = 500001; + + MlAggsHelper.DoubleBucketValues bucketValues = new MlAggsHelper.DoubleBucketValues(null, values); + + ChangeType change = ChangePointDetector.getChangeType(bucketValues); + + assertEquals(12, change.changePoint()); + assertThat(change, instanceOf(ChangeType.Spike.class)); + } +}