This is an automated email from the ASF dual-hosted git repository.
jihao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git
The following commit(s) were added to refs/heads/master by this push:
new 506799f [TE] Percentage change and absolute change rule detector
(#3467)
506799f is described below
commit 506799f336a785f9258aee1a7f0d1fb6fa332479
Author: Jihao Zhang <[email protected]>
AuthorDate: Tue Nov 13 14:21:51 2018 -0800
[TE] Percentage change and absolute change rule detector (#3467)
Absolute change and percentage change anomaly detector.
---
.../components/AbsoluteChangeRuleDetector.java | 101 ++++++++++++++++++
.../components/PercentageChangeRuleDetector.java | 108 +++++++++++++++++++
.../spec/AbsoluteChangeRuleDetectorSpec.java} | 45 +++-----
.../spec/PercentageChangeRuleDetectorSpec.java} | 45 +++-----
.../components/AbsoluteChangeRuleDetectorTest.java | 93 +++++++++++++++++
.../PercentageChangeRuleDetectorTest.java | 114 +++++++++++++++++++++
.../thirdeye/detection/spec/AbstractSpecTest.java | 16 ++-
.../linkedin/thirdeye/detection/spec/TestSpec.java | 10 ++
8 files changed, 470 insertions(+), 62 deletions(-)
diff --git
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleDetector.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleDetector.java
new file mode 100644
index 0000000..a8008b8
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleDetector.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014-2018 LinkedIn Corp. ([email protected])
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.linkedin.thirdeye.detection.components;
+
+import com.linkedin.thirdeye.dashboard.resources.v2.BaselineParsingUtils;
+import com.linkedin.thirdeye.dataframe.BooleanSeries;
+import com.linkedin.thirdeye.dataframe.DataFrame;
+import com.linkedin.thirdeye.dataframe.util.MetricSlice;
+import com.linkedin.thirdeye.datalayer.dto.DatasetConfigDTO;
+import com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO;
+import com.linkedin.thirdeye.detection.DetectionUtils;
+import com.linkedin.thirdeye.detection.InputDataFetcher;
+import com.linkedin.thirdeye.detection.annotation.Components;
+import com.linkedin.thirdeye.detection.annotation.DetectionTag;
+import com.linkedin.thirdeye.detection.annotation.Param;
+import com.linkedin.thirdeye.detection.annotation.PresentationOption;
+import com.linkedin.thirdeye.detection.spec.AbsoluteChangeRuleDetectorSpec;
+import com.linkedin.thirdeye.detection.spi.components.AnomalyDetector;
+import com.linkedin.thirdeye.detection.spi.model.InputData;
+import com.linkedin.thirdeye.detection.spi.model.InputDataSpec;
+import com.linkedin.thirdeye.rootcause.impl.MetricEntity;
+import com.linkedin.thirdeye.rootcause.timeseries.Baseline;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.joda.time.Interval;
+
+import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
+
+
+@Components(title = "Absolute change rule detection",
+ type = "ABSOLUTE_CHANGE_RULE",
+ tags = {DetectionTag.RULE_DETECTION},
+ presentation = {
+ @PresentationOption(name = "absolute value", template = "comparing
${offset} is more than ${difference}"),
+ },
+ params = {
+ @Param(name = "offset", defaultValue = "wo1w"),
+ @Param(name = "change", placeholder = "value")
+ })
+public class AbsoluteChangeRuleDetector implements
AnomalyDetector<AbsoluteChangeRuleDetectorSpec> {
+ private double absoluteChange;
+ private InputDataFetcher dataFetcher;
+ private Baseline baseline;
+ private static final String COL_CURR = "current";
+ private static final String COL_BASE = "baseline";
+ private static final String COL_CHANGE = "change";
+ private static final String COL_ANOMALY = "anomaly";
+ private static final String COL_DIFF = "diff";
+
+ @Override
+ public List<MergedAnomalyResultDTO> runDetection(Interval window, String
metricUrn) {
+ MetricEntity me = MetricEntity.fromURN(metricUrn);
+ MetricSlice slice = MetricSlice.from(me.getId(), window.getStartMillis(),
window.getEndMillis(), me.getFilters());
+ List<MetricSlice> slices = new ArrayList<>(this.baseline.scatter(slice));
+ slices.add(slice);
+ InputData data = this.dataFetcher.fetchData(new
InputDataSpec().withTimeseriesSlices(slices).withMetricIdsForDataset(
+ Collections.singletonList(slice.getMetricId())));
+ DataFrame dfCurr = data.getTimeseries().get(slice).renameSeries(COL_VALUE,
COL_CURR);
+ DataFrame dfBase = this.baseline.gather(slice,
data.getTimeseries()).renameSeries(COL_VALUE, COL_BASE);
+
+ DataFrame df = new DataFrame(dfCurr).addSeries(dfBase);
+ // calculate absolute change
+ df.addSeries(COL_DIFF, df.getDoubles(COL_CURR).subtract(df.get(COL_BASE)));
+
+ // defaults
+ df.addSeries(COL_ANOMALY, BooleanSeries.fillValues(df.size(), false));
+
+ // absolute change
+ if (!Double.isNaN(this.absoluteChange)) {
+ df.addSeries(COL_ANOMALY,
df.getDoubles(COL_DIFF).abs().gte(this.absoluteChange));
+ }
+
+ // make anomalies
+ DatasetConfigDTO datasetConfig =
data.getDatasetForMetricId().get(me.getId());
+ return DetectionUtils.makeAnomalies(slice, df, COL_ANOMALY,
window.getEndMillis(), datasetConfig);
+ }
+
+ @Override
+ public void init(AbsoluteChangeRuleDetectorSpec spec, InputDataFetcher
dataFetcher) {
+ this.absoluteChange = spec.getAbsoluteChangeChange();
+ this.dataFetcher = dataFetcher;
+ String timezone = spec.getTimezone();
+ String offset = spec.getOffset();
+ this.baseline = BaselineParsingUtils.parseOffset(offset, timezone);
+ }
+}
diff --git
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleDetector.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleDetector.java
new file mode 100644
index 0000000..1814e6c
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleDetector.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014-2018 LinkedIn Corp. ([email protected])
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.linkedin.thirdeye.detection.components;
+
+import com.linkedin.thirdeye.dashboard.resources.v2.BaselineParsingUtils;
+import com.linkedin.thirdeye.dataframe.BooleanSeries;
+import com.linkedin.thirdeye.dataframe.DataFrame;
+import com.linkedin.thirdeye.dataframe.Series;
+import com.linkedin.thirdeye.dataframe.util.MetricSlice;
+import com.linkedin.thirdeye.datalayer.dto.DatasetConfigDTO;
+import com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO;
+import com.linkedin.thirdeye.detection.DetectionUtils;
+import com.linkedin.thirdeye.detection.InputDataFetcher;
+import com.linkedin.thirdeye.detection.annotation.Components;
+import com.linkedin.thirdeye.detection.annotation.DetectionTag;
+import com.linkedin.thirdeye.detection.annotation.Param;
+import com.linkedin.thirdeye.detection.annotation.PresentationOption;
+import com.linkedin.thirdeye.detection.spec.PercentageChangeRuleDetectorSpec;
+import com.linkedin.thirdeye.detection.spi.components.AnomalyDetector;
+import com.linkedin.thirdeye.detection.spi.model.InputData;
+import com.linkedin.thirdeye.detection.spi.model.InputDataSpec;
+import com.linkedin.thirdeye.rootcause.impl.MetricEntity;
+import com.linkedin.thirdeye.rootcause.timeseries.Baseline;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.joda.time.Interval;
+
+import static com.linkedin.thirdeye.dataframe.DoubleSeries.*;
+import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
+
+@Components(title = "Percentage change rule detection",
+ type = "PERCENTAGE_RULE",
+ tags = {DetectionTag.RULE_DETECTION},
+ description = "Computes a multi-week aggregate baseline and compares the
current value "
+ + "based on relative change.",
+ presentation = {
+ @PresentationOption(name = "percentage change", template = "comparing
${offset} is more than ${change}")
+ },
+ params = {
+ @Param(name = "offset", defaultValue = "wo1w"),
+ @Param(name = "change", placeholder = "value")
+ })
+public class PercentageChangeRuleDetector implements
AnomalyDetector<PercentageChangeRuleDetectorSpec> {
+ private double percentageChange;
+ private InputDataFetcher dataFetcher;
+ private Baseline baseline;
+ private static final String COL_CURR = "current";
+ private static final String COL_BASE = "baseline";
+ private static final String COL_CHANGE = "change";
+ private static final String COL_ANOMALY = "anomaly";
+
+ @Override
+ public List<MergedAnomalyResultDTO> runDetection(Interval window, String
metricUrn) {
+ MetricEntity me = MetricEntity.fromURN(metricUrn);
+ MetricSlice slice = MetricSlice.from(me.getId(), window.getStartMillis(),
window.getEndMillis(), me.getFilters());
+ List<MetricSlice> slices = new ArrayList<>(this.baseline.scatter(slice));
+ slices.add(slice);
+ InputData data = this.dataFetcher.fetchData(new
InputDataSpec().withTimeseriesSlices(slices).withMetricIdsForDataset(Collections.singletonList(slice.getMetricId())));
+ DataFrame dfCurr = data.getTimeseries().get(slice).renameSeries(COL_VALUE,
COL_CURR);
+ DataFrame dfBase = this.baseline.gather(slice,
data.getTimeseries()).renameSeries(COL_VALUE, COL_BASE);
+
+ DataFrame df = new DataFrame(dfCurr).addSeries(dfBase);
+
+ // calculate percentage change
+ df.addSeries(COL_CHANGE, map((Series.DoubleFunction) values -> {
+ if (values[1] == 0) {
+ return values[0] > 0 ? Double.POSITIVE_INFINITY :
Double.NEGATIVE_INFINITY;
+ }
+ return values[0] / values[1];
+ }, df.getDoubles(COL_CURR), df.get(COL_BASE)).subtract(1));
+
+ // defaults
+ df.addSeries(COL_ANOMALY, BooleanSeries.fillValues(df.size(), false));
+
+ // relative change
+ if (!Double.isNaN(this.percentageChange)) {
+ df.addSeries(COL_ANOMALY,
df.getDoubles(COL_CHANGE).abs().gte(this.percentageChange));
+ }
+
+ // anomalies
+ DatasetConfigDTO datasetConfig =
data.getDatasetForMetricId().get(me.getId());
+ return DetectionUtils.makeAnomalies(slice, df, COL_ANOMALY,
window.getEndMillis(), datasetConfig);
+ }
+
+ @Override
+ public void init(PercentageChangeRuleDetectorSpec spec, InputDataFetcher
dataFetcher) {
+ this.percentageChange = spec.getPercentageChange();
+ this.dataFetcher = dataFetcher;
+ String timezone = spec.getTimezone();
+ String offset = spec.getOffset();
+ this.baseline = BaselineParsingUtils.parseOffset(offset, timezone);
+ }
+}
diff --git
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/AbsoluteChangeRuleDetectorSpec.java
similarity index 50%
copy from
thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
copy to
thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/AbsoluteChangeRuleDetectorSpec.java
index f6ed4b7..c69659e 100644
---
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/AbsoluteChangeRuleDetectorSpec.java
@@ -16,45 +16,32 @@
package com.linkedin.thirdeye.detection.spec;
-import com.linkedin.thirdeye.detection.components.RuleBaselineProvider;
+public class AbsoluteChangeRuleDetectorSpec extends AbstractSpec {
+ private double absoluteChange = Double.NaN;
+ private String offset = "wo1w";
+ private String timezone = "UTC";
-
-public class TestSpec extends AbstractSpec{
- private int a = 123;
- private double b = 456.7;
- private String c = "default";
- private RuleBaselineProvider baselineProvider;
-
- public RuleBaselineProvider getBaselineProvider() {
- return baselineProvider;
- }
-
- public void setBaselineProvider(RuleBaselineProvider baselineProvider) {
- this.baselineProvider = baselineProvider;
+ public String getTimezone() {
+ return timezone;
}
- public int getA() {
- return a;
+ public void setTimezone(String timezone) {
+ this.timezone = timezone;
}
- public void setA(int a) {
- this.a = a;
+ public String getOffset() {
+ return offset;
}
- public double getB() {
- return b;
+ public void setOffset(String offset) {
+ this.offset = offset;
}
- public void setB(double b) {
- this.b = b;
+ public double getAbsoluteChangeChange() {
+ return absoluteChange;
}
- public String getC() {
- return c;
- }
-
- public void setC(String c) {
- this.c = c;
+ public void setAbsoluteChangeChange(double absoluteChange) {
+ this.absoluteChange = absoluteChange;
}
}
-
diff --git
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/PercentageChangeRuleDetectorSpec.java
similarity index 50%
copy from
thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
copy to
thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/PercentageChangeRuleDetectorSpec.java
index f6ed4b7..c1e78fb 100644
---
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/PercentageChangeRuleDetectorSpec.java
@@ -16,45 +16,32 @@
package com.linkedin.thirdeye.detection.spec;
-import com.linkedin.thirdeye.detection.components.RuleBaselineProvider;
+public class PercentageChangeRuleDetectorSpec extends AbstractSpec {
+ private double percentageChange = Double.NaN;
+ private String offset = "wo1w";
+ private String timezone = "UTC";
-
-public class TestSpec extends AbstractSpec{
- private int a = 123;
- private double b = 456.7;
- private String c = "default";
- private RuleBaselineProvider baselineProvider;
-
- public RuleBaselineProvider getBaselineProvider() {
- return baselineProvider;
- }
-
- public void setBaselineProvider(RuleBaselineProvider baselineProvider) {
- this.baselineProvider = baselineProvider;
+ public String getTimezone() {
+ return timezone;
}
- public int getA() {
- return a;
+ public void setTimezone(String timezone) {
+ this.timezone = timezone;
}
- public void setA(int a) {
- this.a = a;
+ public String getOffset() {
+ return offset;
}
- public double getB() {
- return b;
+ public void setOffset(String offset) {
+ this.offset = offset;
}
- public void setB(double b) {
- this.b = b;
+ public double getPercentageChange() {
+ return percentageChange;
}
- public String getC() {
- return c;
- }
-
- public void setC(String c) {
- this.c = c;
+ public void setPercentageChange(double percentageChange) {
+ this.percentageChange = percentageChange;
}
}
-
diff --git
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleDetectorTest.java
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleDetectorTest.java
new file mode 100644
index 0000000..fc73e8d
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleDetectorTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014-2018 LinkedIn Corp. ([email protected])
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.linkedin.thirdeye.detection.components;
+
+import com.linkedin.thirdeye.dataframe.DataFrame;
+import com.linkedin.thirdeye.dataframe.util.MetricSlice;
+import com.linkedin.thirdeye.datalayer.dto.DatasetConfigDTO;
+import com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO;
+import com.linkedin.thirdeye.datalayer.dto.MetricConfigDTO;
+import com.linkedin.thirdeye.detection.DataProvider;
+import com.linkedin.thirdeye.detection.DefaultInputDataFetcher;
+import com.linkedin.thirdeye.detection.InputDataFetcher;
+import com.linkedin.thirdeye.detection.MockDataProvider;
+import com.linkedin.thirdeye.detection.algorithm.AlgorithmUtils;
+import com.linkedin.thirdeye.detection.spec.AbsoluteChangeRuleDetectorSpec;
+import com.linkedin.thirdeye.detection.spec.PercentageChangeRuleDetectorSpec;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.joda.time.Interval;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
+
+
+public class AbsoluteChangeRuleDetectorTest {
+ private DataProvider provider;
+ private DataFrame data;
+
+ @BeforeMethod
+ public void beforeMethod() throws Exception {
+ try (Reader dataReader = new
InputStreamReader(AlgorithmUtils.class.getResourceAsStream("timeseries-4w.csv")))
{
+ this.data = DataFrame.fromCsv(dataReader);
+ this.data.setIndex(COL_TIME);
+ this.data.addSeries(COL_TIME,
this.data.getLongs(COL_TIME).multiply(1000));
+ }
+
+ MetricConfigDTO metricConfigDTO = new MetricConfigDTO();
+ metricConfigDTO.setId(1L);
+ metricConfigDTO.setName("thirdeye-test");
+ metricConfigDTO.setDataset("thirdeye-test-dataset");
+
+ DatasetConfigDTO datasetConfigDTO = new DatasetConfigDTO();
+ datasetConfigDTO.setTimeUnit(TimeUnit.HOURS);
+ datasetConfigDTO.setDataset("thirdeye-test-dataset");
+ datasetConfigDTO.setTimeDuration(1);
+
+ Map<MetricSlice, DataFrame> timeseries = new HashMap<>();
+ timeseries.put(MetricSlice.from(1L, 0L, 604800000L), this.data);
+ timeseries.put(MetricSlice.from(1L, 604800000L, 1209600000L), this.data);
+ timeseries.put(MetricSlice.from(1L, 1209600000L, 1814400000L), this.data);
+ timeseries.put(MetricSlice.from(1L, 1814400000L, 2419200000L), this.data);
+
+ this.provider = new MockDataProvider()
+ .setTimeseries(timeseries)
+ .setMetrics(Collections.singletonList(metricConfigDTO))
+ .setDatasets(Collections.singletonList(datasetConfigDTO));;
+ }
+
+ @Test
+ public void testWeekOverWeekDifference() throws Exception {
+ AbsoluteChangeRuleDetector detector = new AbsoluteChangeRuleDetector();
+ AbsoluteChangeRuleDetectorSpec spec = new AbsoluteChangeRuleDetectorSpec();
+ spec.setAbsoluteChangeChange(400);
+ detector.init(spec, new DefaultInputDataFetcher(this.provider, -1));
+ List<MergedAnomalyResultDTO> anomalies = detector.runDetection(new
Interval(1814400000L, 2419200000L), "thirdeye:metric:1");
+
+ Assert.assertEquals(anomalies.size(), 1);
+ Assert.assertEquals(anomalies.get(0).getStartTime(), 2372400000L);
+ Assert.assertEquals(anomalies.get(0).getEndTime(), 2376000000L);
+ }
+
+}
\ No newline at end of file
diff --git
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleDetectorTest.java
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleDetectorTest.java
new file mode 100644
index 0000000..b1e878c
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleDetectorTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014-2018 LinkedIn Corp. ([email protected])
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.linkedin.thirdeye.detection.components;
+
+import com.linkedin.thirdeye.dataframe.DataFrame;
+import com.linkedin.thirdeye.dataframe.util.MetricSlice;
+import com.linkedin.thirdeye.datalayer.dto.DatasetConfigDTO;
+import com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO;
+import com.linkedin.thirdeye.datalayer.dto.MetricConfigDTO;
+import com.linkedin.thirdeye.detection.DataProvider;
+import com.linkedin.thirdeye.detection.DefaultInputDataFetcher;
+import com.linkedin.thirdeye.detection.InputDataFetcher;
+import com.linkedin.thirdeye.detection.MockDataProvider;
+import com.linkedin.thirdeye.detection.algorithm.AlgorithmUtils;
+import com.linkedin.thirdeye.detection.spec.PercentageChangeRuleDetectorSpec;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.joda.time.Interval;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
+
+
+public class PercentageChangeRuleDetectorTest {
+
+ private DataProvider provider;
+ private DataFrame data;
+
+ @BeforeMethod
+ public void beforeMethod() throws Exception {
+ try (Reader dataReader = new
InputStreamReader(AlgorithmUtils.class.getResourceAsStream("timeseries-4w.csv")))
{
+ this.data = DataFrame.fromCsv(dataReader);
+ this.data.setIndex(COL_TIME);
+ this.data.addSeries(COL_TIME,
this.data.getLongs(COL_TIME).multiply(1000));
+ }
+
+ MetricConfigDTO metricConfigDTO = new MetricConfigDTO();
+ metricConfigDTO.setId(1L);
+ metricConfigDTO.setName("thirdeye-test");
+ metricConfigDTO.setDataset("thirdeye-test-dataset");
+
+ DatasetConfigDTO datasetConfigDTO = new DatasetConfigDTO();
+ datasetConfigDTO.setTimeUnit(TimeUnit.HOURS);
+ datasetConfigDTO.setDataset("thirdeye-test-dataset");
+ datasetConfigDTO.setTimeDuration(1);
+
+ Map<MetricSlice, DataFrame> timeseries = new HashMap<>();
+ timeseries.put(MetricSlice.from(1L, 0L, 604800000L), this.data);
+ timeseries.put(MetricSlice.from(1L, 604800000L, 1209600000L), this.data);
+ timeseries.put(MetricSlice.from(1L, 1209600000L, 1814400000L), this.data);
+ timeseries.put(MetricSlice.from(1L, 1814400000L, 2419200000L), this.data);
+
+ this.provider = new MockDataProvider()
+ .setTimeseries(timeseries)
+ .setMetrics(Collections.singletonList(metricConfigDTO))
+ .setDatasets(Collections.singletonList(datasetConfigDTO));
+ }
+
+ @Test
+ public void testWeekOverWeekChange() throws Exception {
+ PercentageChangeRuleDetector detector = new PercentageChangeRuleDetector();
+ PercentageChangeRuleDetectorSpec spec = new
PercentageChangeRuleDetectorSpec();
+ spec.setPercentageChange(0.4);
+ detector.init(spec, new DefaultInputDataFetcher(this.provider, -1));
+ List<MergedAnomalyResultDTO> anomalies = detector.runDetection(new
Interval(1814400000L, 2419200000L), "thirdeye:metric:1");
+ Assert.assertEquals(anomalies.size(), 2);
+ Assert.assertEquals(anomalies.get(0).getStartTime(), 2372400000L);
+ Assert.assertEquals(anomalies.get(0).getEndTime(), 2376000000L);
+ Assert.assertEquals(anomalies.get(1).getStartTime(), 2379600000L);
+ Assert.assertEquals(anomalies.get(1).getEndTime(), 2383200000L);
+ }
+
+ @Test
+ public void testThreeWeekMedianChange() throws Exception {
+ PercentageChangeRuleDetector detector = new PercentageChangeRuleDetector();
+ PercentageChangeRuleDetectorSpec spec = new
PercentageChangeRuleDetectorSpec();
+ spec.setPercentageChange(0.3);
+ spec.setOffset("median3w");
+ detector.init(spec, new DefaultInputDataFetcher(this.provider, -1));
+ List<MergedAnomalyResultDTO> anomalies = detector.runDetection(new
Interval(1814400000L, 2419200000L), "thirdeye:metric:1");
+ Assert.assertEquals(anomalies.size(), 5);
+ Assert.assertEquals(anomalies.get(0).getStartTime(), 2005200000L);
+ Assert.assertEquals(anomalies.get(0).getEndTime(), 2008800000L);
+ Assert.assertEquals(anomalies.get(1).getStartTime(), 2134800000L);
+ Assert.assertEquals(anomalies.get(1).getEndTime(), 2138400000L);
+ Assert.assertEquals(anomalies.get(2).getStartTime(), 2152800000L);
+ Assert.assertEquals(anomalies.get(2).getEndTime(), 2156400000L);
+ Assert.assertEquals(anomalies.get(3).getStartTime(), 2181600000L);
+ Assert.assertEquals(anomalies.get(3).getEndTime(), 2185200000L);
+ Assert.assertEquals(anomalies.get(4).getStartTime(), 2322000000L);
+ Assert.assertEquals(anomalies.get(4).getEndTime(), 2325600000L);
+ }
+}
\ No newline at end of file
diff --git
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/AbstractSpecTest.java
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/AbstractSpecTest.java
index 70d3aa9..ad871f4 100644
---
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/AbstractSpecTest.java
+++
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/AbstractSpecTest.java
@@ -24,7 +24,7 @@ import org.testng.annotations.Test;
public class AbstractSpecTest {
@Test
- public void testAbstractSpecMapping() {
+ public void testAbstractSpecMappingDefaultValue() {
TestSpec spec = AbstractSpec.fromProperties(ImmutableMap.of(),
TestSpec.class);
Assert.assertEquals(spec.getA(), 123);
Assert.assertEquals(spec.getB(), 456.7);
@@ -32,7 +32,7 @@ public class AbstractSpecTest {
}
@Test
- public void testAbstractSpecMapping1() {
+ public void testAbstractSpecMappingIncompleteProperty() {
TestSpec spec = AbstractSpec.fromProperties(ImmutableMap.of("a", 321),
TestSpec.class);
Assert.assertEquals(spec.getA(), 321);
Assert.assertEquals(spec.getB(), 456.7);
@@ -40,7 +40,7 @@ public class AbstractSpecTest {
}
@Test
- public void testAbstractSpecMapping2() {
+ public void testAbstractSpecMappingNestedObject() {
RuleBaselineProvider provider = new RuleBaselineProvider();
TestSpec spec =
AbstractSpec.fromProperties(ImmutableMap.of("baselineProvider", provider),
TestSpec.class);
Assert.assertEquals(spec.getA(), 123);
@@ -50,12 +50,20 @@ public class AbstractSpecTest {
}
@Test
- public void testAbstractSpecMapping3() {
+ public void testAbstractSpecMappingExtraField() {
TestSpec spec = AbstractSpec.fromProperties(ImmutableMap.of("a", 321,
"className", "org.test.Test"), TestSpec.class);
Assert.assertEquals(spec.getA(), 321);
Assert.assertEquals(spec.getB(), 456.7);
Assert.assertEquals(spec.getC(), "default");
}
+ @Test
+ public void testAbstractSpecMappingNestedMap() {
+ TestSpec spec = AbstractSpec.fromProperties(ImmutableMap.of("a", 321,
"className", "org.test.Test", "configuration", ImmutableMap.of("k1", "v1",
"k2", "v2")), TestSpec.class);
+ Assert.assertEquals(spec.getA(), 321);
+ Assert.assertEquals(spec.getB(), 456.7);
+ Assert.assertEquals(spec.getC(), "default");
+ Assert.assertEquals(spec.getConfiguration(), ImmutableMap.of("k1", "v1",
"k2", "v2"));
+ }
}
diff --git
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
index f6ed4b7..29990b0 100644
---
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
+++
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/spec/TestSpec.java
@@ -17,6 +17,7 @@
package com.linkedin.thirdeye.detection.spec;
import com.linkedin.thirdeye.detection.components.RuleBaselineProvider;
+import java.util.Map;
public class TestSpec extends AbstractSpec{
@@ -24,6 +25,15 @@ public class TestSpec extends AbstractSpec{
private double b = 456.7;
private String c = "default";
private RuleBaselineProvider baselineProvider;
+ private Map<String, String> configuration;
+
+ public Map<String, String> getConfiguration() {
+ return configuration;
+ }
+
+ public void setConfiguration(Map<String, String> configuration) {
+ this.configuration = configuration;
+ }
public RuleBaselineProvider getBaselineProvider() {
return baselineProvider;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]