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]

Reply via email to