This is an automated email from the ASF dual-hosted git repository.

apucher 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 4b4224f  [TE] rootcause - baseline monthly, daily, hourly support 
(#3585)
4b4224f is described below

commit 4b4224f3221d8c2ca40ef1a01541095af7b7a259
Author: Alexander Pucher <[email protected]>
AuthorDate: Wed Dec 5 12:25:34 2018 -0800

    [TE] rootcause - baseline monthly, daily, hourly support (#3585)
    
    This PR adds support for other month-over-month, day-over-day, and 
hour-over-hour baselines in the RCA GUI. The UI changes are unobtrusive to 
enable us to pilot these changes with select users
---
 .../rootcause-select-comparison-range/component.js |  39 ++++---
 .../thirdeye-frontend/app/pods/rootcause/route.js  |   2 +-
 thirdeye/thirdeye-frontend/app/utils/rca-utils.js  |  17 ++-
 .../resources/v2/BaselineParsingUtils.java         | 115 ++++++++++++++++++---
 .../thirdeye/dataframe/util/DataFrameUtils.java    |  12 +++
 .../rootcause/timeseries/BaselineAggregate.java    |  74 +++++++++++++
 6 files changed, 228 insertions(+), 31 deletions(-)

diff --git 
a/thirdeye/thirdeye-frontend/app/pods/components/rootcause-select-comparison-range/component.js
 
b/thirdeye/thirdeye-frontend/app/pods/components/rootcause-select-comparison-range/component.js
index 8eddc58..0367de6 100644
--- 
a/thirdeye/thirdeye-frontend/app/pods/components/rootcause-select-comparison-range/component.js
+++ 
b/thirdeye/thirdeye-frontend/app/pods/components/rootcause-select-comparison-range/component.js
@@ -2,8 +2,7 @@ import { computed } from '@ember/object';
 import Component from '@ember/component';
 import $ from 'jquery';
 import {
-  makeTime,
-  dateFormatFull
+  makeTime
 } from 'thirdeye-frontend/utils/rca-utils';
 
 // TODO consolidate rootcause-select-comparison-range2, rootcause-slider
@@ -31,7 +30,7 @@ const namedToEpocMapping = {
 
 
 export default Component.extend({
-  timeFormat: "MMM D, hh:mm a z",//slider
+  timeFormat: "MMM D, hh:mm a z", //slider
   range: null, // [0, 0]
   compareMode: null, // ""
   onChange: null, // func (start, end, compareMode)
@@ -47,16 +46,26 @@ export default Component.extend({
   },
 
   compareModeOptions: [
-    'WoW',
-    'Wo2W',
-    'Wo3W',
-    'Wo4W',
-    'mean4w',
-    'median4w',
-    'min4w',
-    'max4w',
-    'predicted',
-    'none'
+    {
+      groupName: 'Weekly',
+      options: [ 'wo1w', 'wo2w', 'wo3w', 'wo4w', 'mean4w', 'median4w', 
'min4w', 'max4w' ]
+    },
+    {
+      groupName: 'Hourly',
+      options: [ 'ho1h', 'ho2h', 'ho3h', 'ho6h', 'mean6h', 'median6h', 
'min6h', 'max6h' ]
+    },
+    {
+      groupName: 'Daily',
+      options: [ 'do1d', 'do2d', 'do3d', 'do4d', 'mean4d', 'median4d', 
'min4d', 'max4d' ]
+    },
+    {
+      groupName: 'Monthly',
+      options: [ 'mo1m', 'mo2m', 'mo3m', 'mo6m', 'mean6m', 'median6m', 
'min6m', 'max6m' ]
+    },
+    {
+      groupName: 'Algorithm',
+      options: [ 'predicted', 'none' ]
+    }
   ],
 
   minDisplayWindow: computed('displayRange.[]', function() {//display window 
start - slider
@@ -135,7 +144,7 @@ export default Component.extend({
     this.setProperties({
       startFormattedOneWay: this.get('startFormatted'),
       endFormattedOneWay: this.get('endFormatted'),
-      maxDateFormattedOneWay: this.get('maxDateFormatted'),
+      maxDateFormattedOneWay: this.get('maxDateFormatted')
       // granularityOneWay: this.get('granularity')
     });
 
@@ -166,7 +175,7 @@ export default Component.extend({
         to: this.get('maxInvestigatePeriod').format('x')
       });
 
-      // Update the display window's investiation period on the chart
+      // Update the display window's investigation period on the chart
       onChange(makeTime(start).valueOf(), makeTime(end).valueOf(), 
compareMode);
     },
 
diff --git a/thirdeye/thirdeye-frontend/app/pods/rootcause/route.js 
b/thirdeye/thirdeye-frontend/app/pods/rootcause/route.js
index e4ca2d3..1cd28ed 100644
--- a/thirdeye/thirdeye-frontend/app/pods/rootcause/route.js
+++ b/thirdeye/thirdeye-frontend/app/pods/rootcause/route.js
@@ -255,7 +255,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
         makeTime().startOf('day').add(1, 'day').valueOf()
       ],
       granularity: '1_HOURS',
-      compareMode: 'WoW'
+      compareMode: 'wo1w'
     };
 
     // default params
diff --git a/thirdeye/thirdeye-frontend/app/utils/rca-utils.js 
b/thirdeye/thirdeye-frontend/app/utils/rca-utils.js
index ca16f86..b8b47ea 100644
--- a/thirdeye/thirdeye-frontend/app/utils/rca-utils.js
+++ b/thirdeye/thirdeye-frontend/app/utils/rca-utils.js
@@ -395,7 +395,22 @@ export function toBaselineRange(range, offset) {
     mean4w: 1, // default. not fully supported by backend yet
     median4w: 1, // default. not fully supported by backend yet
     min4w: 1, // default. not fully supported by backend yet
-    max4w: 1 // default. not fully supported by backend yet
+    max4w: 1, // default. not fully supported by backend yet
+    ho1h: 1,
+    ho2h: 1,
+    ho3h: 1,
+    median4h: 1,
+    mean4h: 1,
+    do1d: 1,
+    do2d: 1,
+    do3d: 1,
+    median4d: 1,
+    mean4d: 1,
+    mo1m: 1,
+    mo2m: 1,
+    mo3m: 1,
+    median4m: 1,
+    mean4m: 1
   }[offset.toLowerCase()];
 
   if (offsetWeeks === 0) {
diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/dashboard/resources/v2/BaselineParsingUtils.java
 
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/dashboard/resources/v2/BaselineParsingUtils.java
index 1ff9293..56f643a 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/dashboard/resources/v2/BaselineParsingUtils.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/dashboard/resources/v2/BaselineParsingUtils.java
@@ -29,11 +29,36 @@ public class BaselineParsingUtils {
   private static final Pattern PATTERN_NONE = Pattern.compile("none");
   private static final Pattern PATTERN_PREDICTED = 
Pattern.compile("predicted");
   private static final Pattern PATTERN_CURRENT = Pattern.compile("current");
+  private static final Pattern PATTERN_HOUR_OVER_HOUR = 
Pattern.compile("ho([1-9][0-9]*)h");
+  private static final Pattern PATTERN_DAY_OVER_DAY = 
Pattern.compile("do([1-9][0-9]*)d");
   private static final Pattern PATTERN_WEEK_OVER_WEEK = 
Pattern.compile("wo([1-9][0-9]*)w");
-  private static final Pattern PATTERN_MEAN = 
Pattern.compile("mean([1-9][0-9]*)w");
-  private static final Pattern PATTERN_MEDIAN = 
Pattern.compile("median([1-9][0-9]*)w");
-  private static final Pattern PATTERN_MIN = 
Pattern.compile("min([1-9][0-9]*)w");
-  private static final Pattern PATTERN_MAX = 
Pattern.compile("max([1-9][0-9]*)w");
+  private static final Pattern PATTERN_MONTH_OVER_MONTH = 
Pattern.compile("mo([1-9][0-9]*)m");
+  private static final Pattern PATTERN_MEAN = 
Pattern.compile("mean([1-9][0-9]*)(h|d|w|m)");
+  private static final Pattern PATTERN_MEDIAN = 
Pattern.compile("median([1-9][0-9]*)(h|d|w|m)");
+  private static final Pattern PATTERN_MIN = 
Pattern.compile("min([1-9][0-9]*)(h|d|w|m)");
+  private static final Pattern PATTERN_MAX = 
Pattern.compile("max([1-9][0-9]*)(h|d|w|m)");
+
+  private enum Unit {
+    HOUR("h"),
+    DAY("d"),
+    WEEK("w"),
+    MONTH("m");
+
+    final String unit;
+
+    Unit(String unit) {
+      this.unit = unit;
+    }
+
+    static Unit fromUnit(String unit) {
+      for (Unit u : Unit.values()) {
+        if (u.unit.equals(unit)) {
+          return u;
+        }
+      }
+      throw new IllegalArgumentException(String.format("Unknown unit '%s'", 
unit));
+    }
+  }
 
   /**
    * Returns a configured instance of Baseline for the given, named offset. 
The method uses slice and
@@ -43,11 +68,14 @@ public class BaselineParsingUtils {
    * <pre>
    *   current   the time range as specified by start and end)
    *   none      empty time range
+   *   hoXh      hour-over-hour data points with a lag of X hours)
+   *   doXd      day-over-day data points with a lag of X days)
    *   woXw      week-over-week data points with a lag of X weeks)
-   *   meanXw    average of data points from the the past X weeks, with a lag 
of 1 week)
-   *   medianXw  median of data points from the the past X weeks, with a lag 
of 1 week)
-   *   minXw     minimum of data points from the the past X weeks, with a lag 
of 1 week)
-   *   maxXw     maximum of data points from the the past X weeks, with a lag 
of 1 week)
+   *   moXm      month-over-month data points with a lag of X months)
+   *   meanXU    average of data points from the the past X units (hour, day, 
month, week), with a lag of 1 unit)
+   *   medianXU  median of data points from the the past X units (hour, day, 
month, week), with a lag of 1 unit)
+   *   minXU     minimum of data points from the the past X units (hour, day, 
month, week), with a lag of 1 unit)
+   *   maxXU     maximum of data points from the the past X units (hour, day, 
month, week), with a lag of 1 unit)
    * </pre>
    *
    * @param offset offset identifier
@@ -74,31 +102,90 @@ public class BaselineParsingUtils {
       return new BaselineNone();
     }
 
+    BaselineAggregateType agg = null;
+    int shift = Integer.MIN_VALUE;
+    int count = Integer.MIN_VALUE;
+    Unit unit = null;
+
+    Matcher mHourOverHour = PATTERN_HOUR_OVER_HOUR.matcher(offset);
+    if (mHourOverHour.find()) {
+      agg = BaselineAggregateType.SUM;
+      shift = Integer.valueOf(mHourOverHour.group(1));
+      count = 1;
+      unit = Unit.HOUR;
+    }
+
+    Matcher mDayOverDay = PATTERN_DAY_OVER_DAY.matcher(offset);
+    if (mDayOverDay.find()) {
+      agg = BaselineAggregateType.SUM;
+      shift = Integer.valueOf(mDayOverDay.group(1));
+      count = 1;
+      unit = Unit.DAY;
+    }
+
     Matcher mWeekOverWeek = PATTERN_WEEK_OVER_WEEK.matcher(offset);
     if (mWeekOverWeek.find()) {
-      return BaselineAggregate.fromWeekOverWeek(BaselineAggregateType.SUM, 1, 
Integer.valueOf(mWeekOverWeek.group(1)), timeZone);
+      agg = BaselineAggregateType.SUM;
+      shift = Integer.valueOf(mWeekOverWeek.group(1));
+      count = 1;
+      unit = Unit.WEEK;
+    }
+
+    Matcher mMonthOverMonth = PATTERN_MONTH_OVER_MONTH.matcher(offset);
+    if (mMonthOverMonth.find()) {
+      agg = BaselineAggregateType.SUM;
+      shift = Integer.valueOf(mMonthOverMonth.group(1));
+      count = 1;
+      unit = Unit.MONTH;
     }
 
     Matcher mMean = PATTERN_MEAN.matcher(offset);
     if (mMean.find()) {
-      return BaselineAggregate.fromWeekOverWeek(BaselineAggregateType.MEAN, 
Integer.valueOf(mMean.group(1)), 1, timeZone);
+      agg = BaselineAggregateType.MEAN;
+      shift = 1;
+      count = Integer.valueOf(mMean.group(1));
+      unit = Unit.fromUnit(mMean.group(2));
     }
 
     Matcher mMedian = PATTERN_MEDIAN.matcher(offset);
     if (mMedian.find()) {
-      return BaselineAggregate.fromWeekOverWeek(BaselineAggregateType.MEDIAN, 
Integer.valueOf(mMedian.group(1)), 1, timeZone);
+      agg = BaselineAggregateType.MEDIAN;
+      shift = 1;
+      count = Integer.valueOf(mMedian.group(1));
+      unit = Unit.fromUnit(mMedian.group(2));
     }
 
     Matcher mMin = PATTERN_MIN.matcher(offset);
     if (mMin.find()) {
-      return BaselineAggregate.fromWeekOverWeek(BaselineAggregateType.MIN, 
Integer.valueOf(mMin.group(1)), 1, timeZone);
+      agg = BaselineAggregateType.MIN;
+      shift = 1;
+      count = Integer.valueOf(mMin.group(1));
+      unit = Unit.fromUnit(mMin.group(2));
     }
 
     Matcher mMax = PATTERN_MAX.matcher(offset);
     if (mMax.find()) {
-      return BaselineAggregate.fromWeekOverWeek(BaselineAggregateType.MAX, 
Integer.valueOf(mMax.group(1)), 1, timeZone);
+      agg = BaselineAggregateType.MAX;
+      shift = 1;
+      count = Integer.valueOf(mMax.group(1));
+      unit = Unit.fromUnit(mMax.group(2));
+    }
+
+    if (agg == null || unit == null || shift == Integer.MIN_VALUE || count == 
Integer.MIN_VALUE ) {
+      throw new IllegalArgumentException(String.format("Unsupported offset 
'%s'", offset));
     }
 
-    throw new IllegalArgumentException(String.format("Unknown offset '%s'", 
offset));
+    switch (unit) {
+      case HOUR:
+        return BaselineAggregate.fromHourOverHour(agg, count, shift, timeZone);
+      case DAY:
+        return BaselineAggregate.fromDayOverDay(agg, count, shift, timeZone);
+      case WEEK:
+        return BaselineAggregate.fromWeekOverWeek(agg, count, shift, timeZone);
+      case MONTH:
+        return BaselineAggregate.fromMonthOverMonth(agg, count, shift, 
timeZone);
+      default:
+        throw new IllegalArgumentException(String.format("Unsupported unit 
'%s'", unit));
+    }
   }
 }
diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/dataframe/util/DataFrameUtils.java
 
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/dataframe/util/DataFrameUtils.java
index 2d8be7a..36d08f2 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/dataframe/util/DataFrameUtils.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/dataframe/util/DataFrameUtils.java
@@ -443,6 +443,13 @@ public class DataFrameUtils {
       fields.add(DateTimeFieldType.secondOfMinute());
       fields.add(DateTimeFieldType.millisOfSecond());
 
+    } else if (PeriodType.months().equals(type)) {
+      fields.add(DateTimeFieldType.dayOfMonth());
+      fields.add(DateTimeFieldType.hourOfDay());
+      fields.add(DateTimeFieldType.minuteOfHour());
+      fields.add(DateTimeFieldType.secondOfMinute());
+      fields.add(DateTimeFieldType.millisOfSecond());
+
     } else {
       throw new IllegalArgumentException(String.format("Unsupported PeriodType 
'%s'", type));
     }
@@ -450,6 +457,11 @@ public class DataFrameUtils {
     int[] zeros = new int[fields.size()];
     Arrays.fill(zeros, 0);
 
+    // workaround for dayOfMonth > 0 constraint
+    if (PeriodType.months().equals(type)) {
+      zeros[0] = 1;
+    }
+
     return new Partial(fields.toArray(new DateTimeFieldType[fields.size()]), 
zeros);
   }
 
diff --git 
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/rootcause/timeseries/BaselineAggregate.java
 
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/rootcause/timeseries/BaselineAggregate.java
index 4f34804..be929dc 100644
--- 
a/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/rootcause/timeseries/BaselineAggregate.java
+++ 
b/thirdeye/thirdeye-pinot/src/main/java/com/linkedin/thirdeye/rootcause/timeseries/BaselineAggregate.java
@@ -232,6 +232,27 @@ public class BaselineAggregate implements Baseline {
   }
 
   /**
+   * Returns an instance of BaselineAggregate for the specified type and 
{@code numMonths} offsets
+   * computed on a consecutive month-over-month basis starting with a lag of 
{@code offsetMonths}.
+   * <br/><b>NOTE:</b> this will apply DST correction
+   *
+   * @see BaselineAggregateType
+   *
+   * @param type aggregation type
+   * @param numMonths number of consecutive months
+   * @param offsetMonths lag for starting consecutive months
+   * @param timeZone time zone
+   * @return BaselineAggregate with given type and monthly offsets
+   */
+  public static BaselineAggregate fromMonthOverMonth(BaselineAggregateType 
type, int numMonths, int offsetMonths, DateTimeZone timeZone) {
+    List<Period> offsets = new ArrayList<>();
+    for (int i = 0; i < numMonths; i++) {
+      offsets.add(new Period(0, -1 * (i + offsetMonths), 0, 0, 0, 0, 0, 0, 
PeriodType.months()));
+    }
+    return new BaselineAggregate(type, offsets, timeZone, PeriodType.months());
+  }
+
+  /**
    * Returns an instance of BaselineAggregate for the specified type and 
{@code numWeeks} offsets
    * computed on a consecutive week-over-week basis starting with a lag of 
{@code offsetWeeks}.
    * <br/><b>NOTE:</b> this will apply DST correction (modeled as 7 days)
@@ -384,6 +405,25 @@ public class BaselineAggregate implements Baseline {
         }
       };
 
+    } else if (PeriodType.months().equals(this.periodType)) {
+      return new Series.LongFunction() {
+        @Override
+        public long apply(long... values) {
+          DateTime dateTime = new DateTime(values[0], 
BaselineAggregate.this.timeZone);
+          long months = new Period(origin, dateTime, 
BaselineAggregate.this.periodType).getMonths();
+          long days = dateTime.getDayOfMonth() - 1; // workaround for 
dayOfMonth > 0 constraint
+          if (days == dateTime.dayOfMonth().getMaximumValue() - 1) {
+            days = 99;
+          }
+
+          long hours = dateTime.getHourOfDay();
+          long minutes = dateTime.getMinuteOfHour();
+          long seconds = dateTime.getSecondOfMinute();
+          long millis = dateTime.getMillisOfSecond();
+          return months * 100000000000L + days * 1000000000L + hours * 
10000000L + minutes * 100000L + seconds * 1000L + millis;
+        }
+      };
+
     } else {
       throw new IllegalArgumentException(String.format("Unsupported PeriodType 
'%s'", this.periodType));
     }
@@ -469,6 +509,40 @@ public class BaselineAggregate implements Baseline {
         }
       };
 
+    } else if (PeriodType.months().equals(this.periodType)) {
+      return new Series.LongFunction() {
+        @Override
+        public long apply(long... values) {
+          int months = (int) (values[0] / 100000000000L);
+          int days = (int) ((values[0] / 1000000000L) % 100L);
+          int hours = (int) ((values[0] / 10000000L) % 100L);
+          int minutes = (int) ((values[0] / 100000L) % 100L);
+          int seconds = (int) ((values[0] / 1000L) % 100L);
+          int millis = (int) (values[0] % 1000L);
+
+          DateTime originPlusMonth = origin.plusMonths(months);
+
+          // last day of source month
+          if (days >= 99) {
+            days = originPlusMonth.dayOfMonth().getMaximumValue() - 1;
+          }
+
+          // unsupported destination day (e.g. 31st of Feb)
+          if (originPlusMonth.dayOfMonth().getMaximumValue() < 
originPlusMonth.getDayOfMonth() + days) {
+            return LongSeries.NULL;
+          }
+
+          DateTime target = originPlusMonth
+              .plusDays(days)
+              .plusHours(hours)
+              .plusMinutes(minutes)
+              .plusSeconds(seconds)
+              .plusMillis(millis);
+
+          return target.getMillis();
+        }
+      };
+
     } else {
       throw new IllegalArgumentException(String.format("Unsupported PeriodType 
'%s'", this.periodType));
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to