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 0a43bac [TE] Percentage and absolute change rule filter (#3514)
0a43bac is described below
commit 0a43bac18fafa61d4e8e2ad79384dff3c7653dbf
Author: Jihao Zhang <[email protected]>
AuthorDate: Mon Nov 19 13:43:32 2018 -0800
[TE] Percentage and absolute change rule filter (#3514)
Percentage and absolute change rule filter in the new pipeline.
---
.../AbsoluteChangeRuleAnomalyFilter.java | 97 ++++++++++++++++++++++
.../PercentageChangeRuleAnomalyFilter.java | 96 +++++++++++++++++++++
.../SitewideImpactRuleAnomalyFilter.java | 3 +-
.../spec/AbsoluteChangeRuleAnomalyFilterSpec.java | 56 +++++++++++++
.../PercentageChangeRuleAnomalyFilterSpec.java | 56 +++++++++++++
.../AbsoluteChangeRuleAnomalyFilterTest.java | 90 ++++++++++++++++++++
.../PercentageChangeRuleAnomalyFilterTest.java | 89 ++++++++++++++++++++
7 files changed, 486 insertions(+), 1 deletion(-)
diff --git
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleAnomalyFilter.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleAnomalyFilter.java
new file mode 100644
index 0000000..9c7ce49
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleAnomalyFilter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.DataFrame;
+import com.linkedin.thirdeye.dataframe.util.MetricSlice;
+import com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO;
+import com.linkedin.thirdeye.detection.InputDataFetcher;
+import com.linkedin.thirdeye.detection.Pattern;
+import com.linkedin.thirdeye.detection.annotation.Components;
+import com.linkedin.thirdeye.detection.annotation.DetectionTag;
+import
com.linkedin.thirdeye.detection.spec.AbsoluteChangeRuleAnomalyFilterSpec;
+import com.linkedin.thirdeye.detection.spi.components.AnomalyFilter;
+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.List;
+import java.util.Map;
+import org.apache.commons.lang.StringUtils;
+
+import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
+
+/**
+ * Absolute change anomaly filter. Check if the anomaly's absolute change
compared to baseline is above the threshold.
+ * If not, filters the anomaly.
+ */
+@Components(type = "ABSOLUTE_CHANGE_FILTER", tags = {DetectionTag.RULE_FILTER})
+public class AbsoluteChangeRuleAnomalyFilter implements
AnomalyFilter<AbsoluteChangeRuleAnomalyFilterSpec> {
+ private double threshold;
+ private InputDataFetcher dataFetcher;
+ private Baseline baseline;
+ private Pattern pattern;
+
+ @Override
+ public boolean isQualified(MergedAnomalyResultDTO anomaly) {
+ MetricEntity me = MetricEntity.fromURN(anomaly.getMetricUrn());
+ List<MetricSlice> slices = new ArrayList<>();
+ MetricSlice currentSlice =
+ MetricSlice.from(me.getId(), anomaly.getStartTime(),
anomaly.getEndTime(), me.getFilters());
+ slices.add(currentSlice);
+
+ // customize baseline offset
+ MetricSlice baselineSlice = null;
+ if (baseline != null) {
+ baselineSlice = this.baseline.scatter(currentSlice).get(0);
+ slices.add(baselineSlice);
+ }
+
+ Map<MetricSlice, DataFrame> aggregates =
+ this.dataFetcher.fetchData(new
InputDataSpec().withAggregateSlices(slices)).getAggregates();
+
+ double currentValue = getValueFromAggregates(currentSlice, aggregates);
+ double baselineValue =
+ baselineSlice == null ? anomaly.getAvgBaselineVal() :
getValueFromAggregates(baselineSlice, aggregates);
+ // if inconsistent with up/down, filter the anomaly
+ if (!pattern.equals(Pattern.UP_OR_DOWN) && (currentValue < baselineValue
&& pattern.equals(Pattern.UP)) || (
+ currentValue > baselineValue && pattern.equals(Pattern.DOWN))) {
+ return false;
+ }
+ if (Math.abs(currentValue - baselineValue) < this.threshold) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void init(AbsoluteChangeRuleAnomalyFilterSpec spec, InputDataFetcher
dataFetcher) {
+ this.dataFetcher = dataFetcher;
+ this.pattern = Pattern.valueOf(spec.getPattern().toUpperCase());
+ // customize baseline offset
+ if (StringUtils.isNotBlank(spec.getOffset())) {
+ this.baseline = BaselineParsingUtils.parseOffset(spec.getOffset(),
spec.getTimezone());
+ }
+ this.threshold = spec.getThreshold();
+ }
+
+ private double getValueFromAggregates(MetricSlice slice, Map<MetricSlice,
DataFrame> aggregates) {
+ return aggregates.get(slice).getDouble(COL_VALUE, 0);
+ }
+
+}
diff --git
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleAnomalyFilter.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleAnomalyFilter.java
new file mode 100644
index 0000000..49f4331
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleAnomalyFilter.java
@@ -0,0 +1,96 @@
+/*
+ * 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.DataFrame;
+import com.linkedin.thirdeye.dataframe.util.MetricSlice;
+import com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO;
+import com.linkedin.thirdeye.detection.InputDataFetcher;
+import com.linkedin.thirdeye.detection.Pattern;
+import com.linkedin.thirdeye.detection.annotation.Components;
+import com.linkedin.thirdeye.detection.annotation.DetectionTag;
+import
com.linkedin.thirdeye.detection.spec.PercentageChangeRuleAnomalyFilterSpec;
+import com.linkedin.thirdeye.detection.spi.components.AnomalyFilter;
+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.List;
+import java.util.Map;
+import org.apache.commons.lang.StringUtils;
+
+import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
+
+/**
+ * Percentage change anomaly filter. Check if the anomaly's percentage change
compared to baseline is above the threshold.
+ * If not, filters the anomaly.
+ */
+@Components(type = "PERCENTAGE_CHANGE_FILTER", tags =
{DetectionTag.RULE_FILTER})
+public class PercentageChangeRuleAnomalyFilter implements
AnomalyFilter<PercentageChangeRuleAnomalyFilterSpec> {
+ private double threshold;
+ private InputDataFetcher dataFetcher;
+ private Baseline baseline;
+ private Pattern pattern;
+
+ @Override
+ public boolean isQualified(MergedAnomalyResultDTO anomaly) {
+ MetricEntity me = MetricEntity.fromURN(anomaly.getMetricUrn());
+ List<MetricSlice> slices = new ArrayList<>();
+ MetricSlice currentSlice =
+ MetricSlice.from(me.getId(), anomaly.getStartTime(),
anomaly.getEndTime(), me.getFilters());
+ slices.add(currentSlice);
+
+ // customize baseline offset
+ MetricSlice baselineSlice = null;
+ if (baseline != null) {
+ baselineSlice = this.baseline.scatter(currentSlice).get(0);
+ slices.add(baselineSlice);
+ }
+
+ Map<MetricSlice, DataFrame> aggregates =
+ this.dataFetcher.fetchData(new
InputDataSpec().withAggregateSlices(slices)).getAggregates();
+
+ double currentValue = getValueFromAggregates(currentSlice, aggregates);
+ double baselineValue =
+ baselineSlice == null ? anomaly.getAvgBaselineVal() :
getValueFromAggregates(baselineSlice, aggregates);
+ // if inconsistent with up/down, filter the anomaly
+ if (!pattern.equals(Pattern.UP_OR_DOWN) && (currentValue < baselineValue
&& pattern.equals(Pattern.UP)) || (
+ currentValue > baselineValue && pattern.equals(Pattern.DOWN))) {
+ return false;
+ }
+ if (baselineValue != 0 && Math.abs(currentValue / baselineValue - 1) <
this.threshold) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void init(PercentageChangeRuleAnomalyFilterSpec spec,
InputDataFetcher dataFetcher) {
+ this.dataFetcher = dataFetcher;
+ this.pattern = Pattern.valueOf(spec.getPattern().toUpperCase());
+ // customize baseline offset
+ if (StringUtils.isNotBlank(spec.getOffset())) {
+ this.baseline = BaselineParsingUtils.parseOffset(spec.getOffset(),
spec.getTimezone());
+ }
+ this.threshold = spec.getThreshold();
+ }
+
+ private double getValueFromAggregates(MetricSlice slice, Map<MetricSlice,
DataFrame> aggregates) {
+ return aggregates.get(slice).getDouble(COL_VALUE, 0);
+ }
+}
diff --git
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/SitewideImpactRuleAnomalyFilter.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/SitewideImpactRuleAnomalyFilter.java
index 1eee883..e5800b6 100644
---
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/SitewideImpactRuleAnomalyFilter.java
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/components/SitewideImpactRuleAnomalyFilter.java
@@ -23,6 +23,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import org.apache.commons.lang.StringUtils;
import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
@@ -94,7 +95,7 @@ public class SitewideImpactRuleAnomalyFilter implements
AnomalyFilter<SitewideIm
this.pattern = Pattern.valueOf(spec.getPattern().toUpperCase());
// customize baseline offset
- if (!Strings.isNullOrEmpty(spec.getOffset())){
+ if (StringUtils.isNotBlank(spec.getOffset())){
this.baseline = BaselineParsingUtils.parseOffset(spec.getOffset(),
spec.getTimezone());
}
diff --git
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/AbsoluteChangeRuleAnomalyFilterSpec.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/AbsoluteChangeRuleAnomalyFilterSpec.java
new file mode 100644
index 0000000..138ca50
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/AbsoluteChangeRuleAnomalyFilterSpec.java
@@ -0,0 +1,56 @@
+/*
+ * 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.spec;
+
+public class AbsoluteChangeRuleAnomalyFilterSpec extends AbstractSpec {
+ private String timezone = "UTC";
+ private double threshold = Double.NaN;
+ private String offset;
+ private String pattern;
+
+ public String getTimezone() {
+ return timezone;
+ }
+
+ public void setTimezone(String timezone) {
+ this.timezone = timezone;
+ }
+
+ public double getThreshold() {
+ return threshold;
+ }
+
+ public void setThreshold(double threshold) {
+ this.threshold = threshold;
+ }
+
+ public String getOffset() {
+ return offset;
+ }
+
+ public void setOffset(String offset) {
+ this.offset = offset;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+}
diff --git
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/PercentageChangeRuleAnomalyFilterSpec.java
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/PercentageChangeRuleAnomalyFilterSpec.java
new file mode 100644
index 0000000..3717bb6
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/detection/spec/PercentageChangeRuleAnomalyFilterSpec.java
@@ -0,0 +1,56 @@
+/*
+ * 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.spec;
+
+public class PercentageChangeRuleAnomalyFilterSpec extends AbstractSpec {
+ private String timezone = "UTC";
+ private double threshold = Double.NaN;
+ private String offset;
+ private String pattern;
+
+ public String getTimezone() {
+ return timezone;
+ }
+
+ public void setTimezone(String timezone) {
+ this.timezone = timezone;
+ }
+
+ public double getThreshold() {
+ return threshold;
+ }
+
+ public void setThreshold(double threshold) {
+ this.threshold = threshold;
+ }
+
+ public String getOffset() {
+ return offset;
+ }
+
+ public void setOffset(String offset) {
+ this.offset = offset;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+}
diff --git
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleAnomalyFilterTest.java
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleAnomalyFilterTest.java
new file mode 100644
index 0000000..bc0bb29
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/AbsoluteChangeRuleAnomalyFilterTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.MergedAnomalyResultDTO;
+import com.linkedin.thirdeye.detection.DataProvider;
+import com.linkedin.thirdeye.detection.DefaultInputDataFetcher;
+import com.linkedin.thirdeye.detection.DetectionTestUtils;
+import com.linkedin.thirdeye.detection.MockDataProvider;
+import
com.linkedin.thirdeye.detection.spec.AbsoluteChangeRuleAnomalyFilterSpec;
+import
com.linkedin.thirdeye.detection.spec.PercentageChangeRuleAnomalyFilterSpec;
+import com.linkedin.thirdeye.detection.spi.components.AnomalyFilter;
+import com.linkedin.thirdeye.rootcause.timeseries.Baseline;
+import com.linkedin.thirdeye.rootcause.timeseries.BaselineAggregate;
+import com.linkedin.thirdeye.rootcause.timeseries.BaselineAggregateType;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
+
+
+public class AbsoluteChangeRuleAnomalyFilterTest {
+ private static final String METRIC_URN = "thirdeye:metric:123";
+
+ private DataProvider testDataProvider;
+ private Baseline baseline;
+
+ @BeforeMethod
+ public void beforeMethod() {
+ this.baseline =
BaselineAggregate.fromWeekOverWeek(BaselineAggregateType.MEDIAN, 1, 1,
DateTimeZone.forID("UTC"));
+
+ MetricSlice slice1 = MetricSlice.from(123L, 0, 2);
+ MetricSlice baselineSlice1 = this.baseline.scatter(slice1).get(0);
+ MetricSlice slice2 = MetricSlice.from(123L, 4, 6);
+ MetricSlice baselineSlice2 = this.baseline.scatter(slice2).get(0);
+
+ Map<MetricSlice, DataFrame> aggregates = new HashMap<>();
+ aggregates.put(slice1, new DataFrame().addSeries(COL_VALUE, 150));
+ aggregates.put(baselineSlice1, new DataFrame().addSeries(COL_VALUE, 200));
+ aggregates.put(slice2, new DataFrame().addSeries(COL_VALUE, 500));
+ aggregates.put(baselineSlice2, new DataFrame().addSeries(COL_VALUE, 1000));
+
+ this.testDataProvider = new MockDataProvider().setAggregates(aggregates);
+ }
+
+ @Test
+ public void testAbsoluteChangeFilter(){
+ AbsoluteChangeRuleAnomalyFilterSpec spec = new
AbsoluteChangeRuleAnomalyFilterSpec();
+ spec.setOffset("median1w");
+ spec.setThreshold(100);
+ spec.setPattern("up_or_down");
+ AnomalyFilter filter = new AbsoluteChangeRuleAnomalyFilter();
+ filter.init(spec, new DefaultInputDataFetcher(this.testDataProvider,
125L));
+ List<Boolean> results =
+ Arrays.asList(makeAnomaly(0, 2), makeAnomaly(4,
6)).stream().map(anomaly -> filter.isQualified(anomaly)).collect(
+ Collectors.toList());
+ Assert.assertEquals(results, Arrays.asList(false, true));
+ }
+
+
+ private static MergedAnomalyResultDTO makeAnomaly(long start, long end) {
+ Map<String, String> dimensions = new HashMap<>();
+ MergedAnomalyResultDTO anomaly = DetectionTestUtils.makeAnomaly(125L,
start, end, dimensions);
+ anomaly.setMetricUrn(METRIC_URN);
+ return anomaly;
+ }
+}
diff --git
a/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleAnomalyFilterTest.java
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleAnomalyFilterTest.java
new file mode 100644
index 0000000..a4a65bb
--- /dev/null
+++
b/thirdeye/thirdeye-pinot/src/test/java/com/linkedin/thirdeye/detection/components/PercentageChangeRuleAnomalyFilterTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.MergedAnomalyResultDTO;
+import com.linkedin.thirdeye.detection.DataProvider;
+import com.linkedin.thirdeye.detection.DefaultInputDataFetcher;
+import com.linkedin.thirdeye.detection.DetectionTestUtils;
+import com.linkedin.thirdeye.detection.MockDataProvider;
+import
com.linkedin.thirdeye.detection.spec.PercentageChangeRuleAnomalyFilterSpec;
+import com.linkedin.thirdeye.detection.spi.components.AnomalyFilter;
+import com.linkedin.thirdeye.rootcause.timeseries.Baseline;
+import com.linkedin.thirdeye.rootcause.timeseries.BaselineAggregate;
+import com.linkedin.thirdeye.rootcause.timeseries.BaselineAggregateType;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static com.linkedin.thirdeye.dataframe.util.DataFrameUtils.*;
+
+
+public class PercentageChangeRuleAnomalyFilterTest {
+ private static final String METRIC_URN = "thirdeye:metric:123";
+
+ private DataProvider testDataProvider;
+ private Baseline baseline;
+
+ @BeforeMethod
+ public void beforeMethod() {
+ this.baseline =
BaselineAggregate.fromWeekOverWeek(BaselineAggregateType.MEDIAN, 1, 1,
DateTimeZone.forID("UTC"));
+
+ MetricSlice slice1 = MetricSlice.from(123L, 0, 2);
+ MetricSlice baselineSlice1 = this.baseline.scatter(slice1).get(0);
+ MetricSlice slice2 = MetricSlice.from(123L, 4, 6);
+ MetricSlice baselineSlice2 = this.baseline.scatter(slice2).get(0);
+
+ Map<MetricSlice, DataFrame> aggregates = new HashMap<>();
+ aggregates.put(slice1, new DataFrame().addSeries(COL_VALUE, 150));
+ aggregates.put(baselineSlice1, new DataFrame().addSeries(COL_VALUE, 200));
+ aggregates.put(slice2, new DataFrame().addSeries(COL_VALUE, 500));
+ aggregates.put(baselineSlice2, new DataFrame().addSeries(COL_VALUE, 1000));
+
+ this.testDataProvider = new MockDataProvider().setAggregates(aggregates);
+ }
+
+ @Test
+ public void testPercentageChangeFilter(){
+ PercentageChangeRuleAnomalyFilterSpec spec = new
PercentageChangeRuleAnomalyFilterSpec();
+ spec.setOffset("median1w");
+ spec.setThreshold(0.5);
+ spec.setPattern("up_or_down");
+ AnomalyFilter filter = new PercentageChangeRuleAnomalyFilter();
+ filter.init(spec, new DefaultInputDataFetcher(this.testDataProvider,
125L));
+ List<Boolean> results =
+ Arrays.asList(makeAnomaly(0, 2), makeAnomaly(4,
6)).stream().map(anomaly -> filter.isQualified(anomaly)).collect(
+ Collectors.toList());
+ Assert.assertEquals(results, Arrays.asList(false, true));
+ }
+
+
+ private static MergedAnomalyResultDTO makeAnomaly(long start, long end) {
+ Map<String, String> dimensions = new HashMap<>();
+ MergedAnomalyResultDTO anomaly = DetectionTestUtils.makeAnomaly(125L,
start, end, dimensions);
+ anomaly.setMetricUrn(METRIC_URN);
+ return anomaly;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]