New perf dashboard shows too much space around interesting data points
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Feb 2015 22:56:31 +0000 (22:56 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Feb 2015 22:56:31 +0000 (22:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=141487

Reviewed by Chris Dumez.

Revise the y-axis range adjustment algorithm in r179913. Instead of showing the entire moving average,
show the current time series excluding points in the series outside the moving average envelope.

* public/v2/app.js:
(App.Pane._computeChartData): Don't deal with missing moving average or enveloping strategy here.
(App.Pane._computeMovingAverageAndOutliers): Set isOutliner to true on all data points in the current
time series if the point lies outside the moving average envelope. Don't expose the moving average or
the envelope computed for this purpose if they're not set by the user.

* public/v2/data.js:
(TimeSeries.prototype.minMaxForTimeRange): Takes a boolean argument, ignoreOutlier. When the flag is set
to true, min/max computation will ignore any point in the series with non-falsy "isOutliner" property.

* public/v2/interactive-chart.js:
(App.InteractiveChartComponent._constructGraphIfPossible): Unsupport hideMovingAverage and hideEnvelope.
(App.InteractiveChartComponent._computeYAxisDomain): Removed the commented out code. Also moved the code
to deal with showFullYAxis here.
(App.InteractiveChartComponent._minMaxForAllTimeSeries): Rewrote the code. Takes ignoreOutliners as an
argument instead of directly inspecting showFullYAxis.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@179965 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Websites/perf.webkit.org/ChangeLog
Websites/perf.webkit.org/public/v2/app.js
Websites/perf.webkit.org/public/v2/data.js
Websites/perf.webkit.org/public/v2/interactive-chart.js

index 3990430..9a46307 100644 (file)
@@ -1,3 +1,30 @@
+2015-02-11  Ryosuke Niwa  <rniwa@webkit.org>
+
+        New perf dashboard shows too much space around interesting data points
+        https://bugs.webkit.org/show_bug.cgi?id=141487
+
+        Reviewed by Chris Dumez.
+
+        Revise the y-axis range adjustment algorithm in r179913. Instead of showing the entire moving average,
+        show the current time series excluding points in the series outside the moving average envelope.
+
+        * public/v2/app.js:
+        (App.Pane._computeChartData): Don't deal with missing moving average or enveloping strategy here.
+        (App.Pane._computeMovingAverageAndOutliers): Set isOutliner to true on all data points in the current
+        time series if the point lies outside the moving average envelope. Don't expose the moving average or
+        the envelope computed for this purpose if they're not set by the user.
+
+        * public/v2/data.js:
+        (TimeSeries.prototype.minMaxForTimeRange): Takes a boolean argument, ignoreOutlier. When the flag is set
+        to true, min/max computation will ignore any point in the series with non-falsy "isOutliner" property.
+
+        * public/v2/interactive-chart.js:
+        (App.InteractiveChartComponent._constructGraphIfPossible): Unsupport hideMovingAverage and hideEnvelope.
+        (App.InteractiveChartComponent._computeYAxisDomain): Removed the commented out code. Also moved the code
+        to deal with showFullYAxis here.
+        (App.InteractiveChartComponent._minMaxForAllTimeSeries): Rewrote the code. Takes ignoreOutliners as an
+        argument instead of directly inspecting showFullYAxis.
+
 2015-02-10  Ryosuke Niwa  <rniwa@webkit.org>
 
         New perf dashboard shouldn't always show outliners
index f80e5e4..4b065ad 100755 (executable)
@@ -484,38 +484,44 @@ App.Pane = Ember.Object.extend({
         var envelopingStrategy = this.get('chosenEnvelopingStrategy');
         this._updateStrategyConfigIfNeeded(envelopingStrategy, 'envelopingConfig');
 
-        if (!movingAverageStrategy || !movingAverageStrategy.execute) {
-            movingAverageStrategy = Statistics.MovingAverageStrategies[0];
-            chartData.hideMovingAverage = true;
-        }
-        if (!envelopingStrategy || !envelopingStrategy.execute) {
-            envelopingStrategy = Statistics.EnvelopingStrategies[0];
-            chartData.hideEnvelope = true;
-        }
-
-        chartData.movingAverage = this._computeMovingAverage(chartData, movingAverageStrategy, envelopingStrategy);
+        chartData.movingAverage = this._computeMovingAverageAndOutliers(chartData, movingAverageStrategy, envelopingStrategy);
 
         this.set('chartData', chartData);
     }.observes('chosenMovingAverageStrategy', 'chosenMovingAverageStrategy.parameterList.@each.value',
         'chosenEnvelopingStrategy', 'chosenEnvelopingStrategy.parameterList.@each.value'),
-    _computeMovingAverage: function (chartData, movingAverageStrategy, envelopingStrategy)
+    _computeMovingAverageAndOutliers: function (chartData, movingAverageStrategy, envelopingStrategy)
     {
         var currentTimeSeriesData = chartData.current.series();
-        var movingAverageValues = this._executeStrategy(movingAverageStrategy, currentTimeSeriesData);
+        var movingAverageIsSetByUser = movingAverageStrategy && movingAverageStrategy.execute;
+        var movingAverageValues = this._executeStrategy(
+            movingAverageIsSetByUser ? movingAverageStrategy : Statistics.MovingAverageStrategies[0], currentTimeSeriesData);
         if (!movingAverageValues)
             return null;
 
-        var envelopeDelta = this._executeStrategy(envelopingStrategy, currentTimeSeriesData, [movingAverageValues]);
+        var envelopeIsSetByUser = envelopingStrategy && envelopingStrategy.execute;
+        var envelopeDelta = this._executeStrategy(envelopeIsSetByUser ? envelopingStrategy : Statistics.EnvelopingStrategies[0],
+            currentTimeSeriesData, [movingAverageValues]);
 
-        return new TimeSeries(currentTimeSeriesData.map(function (point, index) {
-            var value = movingAverageValues[index];
-            return {
-                measurement: point.measurement,
-                time: point.time,
-                value: value,
-                interval: envelopeDelta !== null ? [value - envelopeDelta, value + envelopeDelta] : null,
-            }
-        }));
+        for (var i = 0; i < currentTimeSeriesData.length; i++) {
+            var currentValue = currentTimeSeriesData[i].value;
+            var movingAverageValue = movingAverageValues[i];
+            if (currentValue < movingAverageValue - envelopeDelta || movingAverageValue + envelopeDelta < currentValue)
+                currentTimeSeriesData[i].isOutlier = true;
+        }
+        if (!envelopeIsSetByUser)
+            envelopeDelta = null;
+
+        if (movingAverageIsSetByUser) {
+            return new TimeSeries(currentTimeSeriesData.map(function (point, index) {
+                var value = movingAverageValues[index];
+                return {
+                    measurement: point.measurement,
+                    time: point.time,
+                    value: value,
+                    interval: envelopeDelta !== null ? [value - envelopeDelta, value + envelopeDelta] : null,
+                }
+            }));            
+        }
     },
     _executeStrategy: function (strategy, currentTimeSeriesData, additionalArguments)
     {
index dfe3133..208575e 100755 (executable)
@@ -372,7 +372,7 @@ TimeSeries.prototype.seriesBetweenPoints = function (startPoint, endPoint)
     return this._series.slice(startPoint.seriesIndex, endPoint.seriesIndex + 1);
 }
 
-TimeSeries.prototype.minMaxForTimeRange = function (startTime, endTime)
+TimeSeries.prototype.minMaxForTimeRange = function (startTime, endTime, ignoreOutlier)
 {
     var data = this._series;
     var i = 0;
@@ -389,6 +389,8 @@ TimeSeries.prototype.minMaxForTimeRange = function (startTime, endTime)
     var max = Number.MIN_VALUE;
     for (; i < data.length; i++) {
         var point = data[i];
+        if (point.isOutlier && ignoreOutlier)
+            continue;
         var currentMin = point.interval ? point.interval[0] : point.value;
         var currentMax = point.interval ? point.interval[1] : point.value;
 
index 6887169..1b65626 100644 (file)
@@ -128,7 +128,7 @@ App.InteractiveChartComponent = Ember.Component.extend({
                 .attr("class", "target"));
         }
 
-        var movingAverageIsVisible = this._movingAverageTimeSeries && !chartData.hideMovingAverage;
+        var movingAverageIsVisible = this._movingAverageTimeSeries;
         var foregroundClass = movingAverageIsVisible ? '' : ' foreground';
         this._areas.push(this._clippedContainer
             .append("path")
@@ -153,12 +153,10 @@ App.InteractiveChartComponent = Ember.Component.extend({
                 .datum(this._movingAverageTimeSeries.series())
                 .attr("class", "movingAverage"));
 
-            if (!chartData.hideEnvelope) {
-                this._areas.push(this._clippedContainer
-                    .append("path")
-                    .datum(this._movingAverageTimeSeries.series())
-                    .attr("class", "envelope"));
-            }
+            this._areas.push(this._clippedContainer
+                .append("path")
+                .datum(this._movingAverageTimeSeries.series())
+                .attr("class", "envelope"));
         }
 
         if (isInteractive) {
@@ -336,33 +334,33 @@ App.InteractiveChartComponent = Ember.Component.extend({
     },
     _computeYAxisDomain: function (startTime, endTime)
     {
-        var range = this._minMaxForAllTimeSeries(startTime, endTime);
+        var shouldShowFullYAxis = this.get('showFullYAxis');
+        var range = this._minMaxForAllTimeSeries(startTime, endTime, !shouldShowFullYAxis);
         var min = range[0];
         var max = range[1];
         if (max < min)
             min = max = 0;
+        else if (shouldShowFullYAxis)
+            min = Math.min(min, 0);
         var diff = max - min;
         var margin = diff * 0.05;
 
         yExtent = [min - margin, max + margin];
-//        if (yMin !== undefined)
-//            yExtent[0] = parseInt(yMin);
         return yExtent;
     },
-    _minMaxForAllTimeSeries: function (startTime, endTime)
+    _minMaxForAllTimeSeries: function (startTime, endTime, ignoreOutliners)
     {
-        var shouldShowFullYAxis = this.get('showFullYAxis');
-        var mainTimeSeries = this._movingAverageTimeSeries && !shouldShowFullYAxis ? this._movingAverageTimeSeries : this._currentTimeSeries;
-        var currentRange = mainTimeSeries.minMaxForTimeRange(startTime, endTime);
-        if (shouldShowFullYAxis)
-            currentRange[0] = Math.min(0, currentRange[0]);
-
-        var baselineRange = this._baselineTimeSeries ? this._baselineTimeSeries.minMaxForTimeRange(startTime, endTime) : [Number.MAX_VALUE, Number.MIN_VALUE];
-        var targetRange = this._targetTimeSeries ? this._targetTimeSeries.minMaxForTimeRange(startTime, endTime) : [Number.MAX_VALUE, Number.MIN_VALUE];
-        return [
-            Math.min(currentRange[0], baselineRange[0], targetRange[0]),
-            Math.max(currentRange[1], baselineRange[1], targetRange[1]),
-        ];
+        var seriesList = [this._currentTimeSeries, this._movingAverageTimeSeries, this._baselineTimeSeries, this._targetTimeSeries];
+        var min = Infinity;
+        var max = -Infinity;
+        for (var i = 0; i < seriesList.length; i++) {
+            if (seriesList[i]) {
+                var minMax = seriesList[i].minMaxForTimeRange(startTime, endTime, ignoreOutliners);
+                min = Math.min(min, minMax[0]);
+                max = Math.max(max, minMax[1]);
+            }
+        }
+        return [min, max];
     },
     _currentSelection: function ()
     {