superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From maximebeauche...@apache.org
Subject [incubator-superset] branch master updated: New time_pivot visualization (#3941)
Date Thu, 07 Dec 2017 05:50:35 GMT
This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 5bc581f  New time_pivot visualization (#3941)
5bc581f is described below

commit 5bc581fd442e0bf9308b45c51dad9e01619bcada
Author: Maxime Beauchemin <maximebeauchemin@gmail.com>
AuthorDate: Wed Dec 6 21:50:33 2017 -0800

    New time_pivot visualization (#3941)
    
    * New time_pivot visualization
    
    * Minor tweaks
    
    * Addressing comments
---
 .../assets/images/viz_thumbnails/time_pivot.png    | Bin 0 -> 84481 bytes
 .../javascripts/explore/components/Control.jsx     |   1 +
 .../explore/components/ControlHeader.jsx           |   2 +
 .../assets/javascripts/explore/stores/controls.jsx |  24 ++++++++
 .../assets/javascripts/explore/stores/visTypes.js  |  44 +++++++++++++++
 superset/assets/javascripts/modules/colors.js      |   1 +
 superset/assets/visualizations/main.js             |   1 +
 superset/assets/visualizations/nvd3_vis.js         |  20 ++++++-
 superset/viz.py                                    |  61 +++++++++++++++++++--
 9 files changed, 147 insertions(+), 7 deletions(-)

diff --git a/superset/assets/images/viz_thumbnails/time_pivot.png b/superset/assets/images/viz_thumbnails/time_pivot.png
new file mode 100644
index 0000000..149f3da
Binary files /dev/null and b/superset/assets/images/viz_thumbnails/time_pivot.png differ
diff --git a/superset/assets/javascripts/explore/components/Control.jsx b/superset/assets/javascripts/explore/components/Control.jsx
index e458807..25d69a5 100644
--- a/superset/assets/javascripts/explore/components/Control.jsx
+++ b/superset/assets/javascripts/explore/components/Control.jsx
@@ -13,6 +13,7 @@ const propTypes = {
   label: PropTypes.string.isRequired,
   choices: PropTypes.arrayOf(PropTypes.array),
   description: PropTypes.string,
+  tooltipOnClick: PropTypes.func,
   places: PropTypes.number,
   validators: PropTypes.array,
   validationErrors: PropTypes.array,
diff --git a/superset/assets/javascripts/explore/components/ControlHeader.jsx b/superset/assets/javascripts/explore/components/ControlHeader.jsx
index d2e0a6e..bc474a6 100644
--- a/superset/assets/javascripts/explore/components/ControlHeader.jsx
+++ b/superset/assets/javascripts/explore/components/ControlHeader.jsx
@@ -13,6 +13,7 @@ const propTypes = {
   leftNode: PropTypes.node,
   onClick: PropTypes.func,
   hovered: PropTypes.bool,
+  tooltipOnClick: PropTypes.func,
 };
 
 const defaultProps = {
@@ -32,6 +33,7 @@ export default class ControlHeader extends React.Component {
                 label={t('description')}
                 tooltip={this.props.description}
                 placement="top"
+                onClick={this.props.tooltipOnClick}
               />
               {' '}
             </span>
diff --git a/superset/assets/javascripts/explore/stores/controls.jsx b/superset/assets/javascripts/explore/stores/controls.jsx
index fb65199..810c11d 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -433,6 +433,30 @@ export const controls = {
     'to find in the [country] column'),
   },
 
+  freq: {
+    type: 'SelectControl',
+    label: t('Frequency'),
+    default: 'W-MON',
+    freeForm: true,
+    clearable: false,
+    choices: [
+      ['AS', 'Year (freq=AS)'],
+      ['52W-MON', '52 weeks starting Monday (freq=52W-MON)'],
+      ['W-SUN', '1 week starting Sunday (freq=W-SUN)'],
+      ['W-MON', '1 week starting Monday (freq=W-MON)'],
+      ['D', 'Day (freq=D)'],
+      ['4W-MON', '4 weeks (freq=4W-MON)'],
+    ],
+    description: t(
+      `The periodicity over which to pivot time. Users can provide
+      "Pandas" offset alias.
+      Click on the info bubble for more details on accepted "freq" expressions.`),
+    tooltipOnClick: () => {
+      window.open(
+        'https://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases');
+    },
+  },
+
   groupby: groupByControl,
   dimension: {
     ...groupByControl,
diff --git a/superset/assets/javascripts/explore/stores/visTypes.js b/superset/assets/javascripts/explore/stores/visTypes.js
index 7399bdb..7bae7f2 100644
--- a/superset/assets/javascripts/explore/stores/visTypes.js
+++ b/superset/assets/javascripts/explore/stores/visTypes.js
@@ -195,6 +195,50 @@ export const visTypes = {
     },
   },
 
+  time_pivot: {
+    label: t('Time Series - Periodicity Pivot'),
+    showOnExplore: true,
+    requiresTime: true,
+    controlPanelSections: [
+      {
+        label: t('Query'),
+        expanded: true,
+        controlSetRows: [
+          ['metric', 'freq'],
+        ],
+      },
+      {
+        label: t('Chart Options'),
+        expanded: true,
+        controlSetRows: [
+          ['show_legend', 'line_interpolation'],
+          ['color_picker', null],
+        ],
+      },
+      {
+        label: t('X Axis'),
+        controlSetRows: [
+          ['x_axis_label', 'bottom_margin'],
+          ['x_axis_showminmax', 'x_axis_format'],
+        ],
+      },
+      {
+        label: t('Y Axis'),
+        controlSetRows: [
+          ['y_axis_label', 'left_margin'],
+          ['y_axis_showminmax', 'y_log_scale'],
+          ['y_axis_format', 'y_axis_bounds'],
+        ],
+      },
+    ],
+    controlOverrides: {
+      x_axis_format: {
+        choices: D3_TIME_FORMAT_OPTIONS,
+        default: 'smart_date',
+      },
+    },
+  },
+
   dual_line: {
     label: t('Dual Axis Line Chart'),
     requiresTime: true,
diff --git a/superset/assets/javascripts/modules/colors.js b/superset/assets/javascripts/modules/colors.js
index 641eec2..f2bba3b 100644
--- a/superset/assets/javascripts/modules/colors.js
+++ b/superset/assets/javascripts/modules/colors.js
@@ -1,6 +1,7 @@
 import d3 from 'd3';
 
 export const brandColor = '#00A699';
+export const colorPrimary = { r: 0, g: 122, b: 135, a: 1 };
 
 // Color related utility functions go in this object
 export const bnbColors = [
diff --git a/superset/assets/visualizations/main.js b/superset/assets/visualizations/main.js
index 2afc57b..9976614 100644
--- a/superset/assets/visualizations/main.js
+++ b/superset/assets/visualizations/main.js
@@ -18,6 +18,7 @@ const vizMap = {
   horizon: require('./horizon.js'),
   iframe: require('./iframe.js'),
   line: require('./nvd3_vis.js'),
+  time_pivot: require('./nvd3_vis.js'),
   mapbox: require('./mapbox.jsx'),
   markup: require('./markup.js'),
   para: require('./parallel_coordinates.js'),
diff --git a/superset/assets/visualizations/nvd3_vis.js b/superset/assets/visualizations/nvd3_vis.js
index bbef132..f1b6c11 100644
--- a/superset/assets/visualizations/nvd3_vis.js
+++ b/superset/assets/visualizations/nvd3_vis.js
@@ -166,6 +166,13 @@ function nvd3Vis(slice, payload) {
         chart.xAxis.staggerLabels(false);
         break;
 
+      case 'time_pivot':
+        chart = nv.models.lineChart();
+        chart.xScale(d3.time.scale.utc());
+        chart.interpolate(fd.line_interpolation);
+        chart.xAxis.staggerLabels(false);
+        break;
+
       case 'dual_line':
         chart = nv.models.multiChart();
         chart.interpolate('linear');
@@ -337,7 +344,7 @@ function nvd3Vis(slice, payload) {
       chart.xScale(d3.scale.log());
     }
     const isTimeSeries = [
-      'line', 'dual_line', 'area', 'compare', 'bar'].indexOf(vizType) >= 0;
+      'line', 'dual_line', 'area', 'compare', 'bar', 'time_pivot'].indexOf(vizType) >=
0;
     // if x axis format is a date format, rotate label 90 degrees
     if (isTimeSeries) {
       chart.xAxis.rotateLabels(45);
@@ -375,7 +382,16 @@ function nvd3Vis(slice, payload) {
     setAxisShowMaxMin(chart.yAxis, fd.y_axis_showminmax);
     setAxisShowMaxMin(chart.y2Axis, fd.y_axis_showminmax);
 
-    if (vizType !== 'bullet') {
+    if (vizType === 'time_pivot') {
+      chart.color((d) => {
+        const c = fd.color_picker;
+        let alpha = 1;
+        if (d.rank > 0) {
+          alpha = d.perc * 0.5;
+        }
+        return `rgba(${c.r}, ${c.g}, ${c.b}, ${alpha})`;
+      });
+    } else if (vizType !== 'bullet') {
       chart.color(d => getColorFromScheme(d[colorKey], fd.color_scheme));
     }
     if ((vizType === 'line' || vizType === 'area') && fd.rich_tooltip) {
diff --git a/superset/viz.py b/superset/viz.py
index 6b369be..e3c1737 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -25,6 +25,7 @@ from flask_babel import lazy_gettext as _
 from markdown import markdown
 import numpy as np
 import pandas as pd
+from pandas.tseries.frequencies import to_offset
 import simplejson as json
 from six import PY3, string_types
 from six.moves import reduce
@@ -947,14 +948,23 @@ class NVD3TimeSeriesViz(NVD3Viz):
             elif title_suffix and isinstance(series_title, (list, tuple)):
                 series_title = series_title + (title_suffix,)
 
+            values = []
+            for ds in df.index:
+                if ds in ys:
+                    d = {
+                        'x': ds,
+                        'y': ys[ds],
+                    }
+                else:
+                    d = {}
+                values.append(d)
+
             d = {
                 'key': series_title,
-                'classed': classed,
-                'values': [
-                    {'x': ds, 'y': ys[ds] if ds in ys else None}
-                    for ds in df.index
-                ],
+                'values': values,
             }
+            if classed:
+                d['classed'] = classed
             chart_data.append(d)
         return chart_data
 
@@ -1136,6 +1146,47 @@ class NVD3TimeSeriesBarViz(NVD3TimeSeriesViz):
     verbose_name = _('Time Series - Bar Chart')
 
 
+class NVD3TimePivotViz(NVD3TimeSeriesViz):
+
+    """Time Series - Periodicity Pivot"""
+
+    viz_type = 'time_pivot'
+    sort_series = True
+    verbose_name = _('Time Series - Period Pivot')
+
+    def query_obj(self):
+        d = super(NVD3TimePivotViz, self).query_obj()
+        d['metrics'] = [self.form_data.get('metric')]
+        return d
+
+    def get_data(self, df):
+        fd = self.form_data
+        df = self.process_data(df)
+        freq = to_offset(fd.get('freq'))
+        freq.normalize = True
+        df[DTTM_ALIAS] = df.index.map(freq.rollback)
+        df['ranked'] = df[DTTM_ALIAS].rank(method='dense', ascending=False) - 1
+        df.ranked = df.ranked.map(int)
+        df['series'] = '-' + df.ranked.map(str)
+        df['series'] = df['series'].str.replace('-0', 'current')
+        rank_lookup = {
+            row['series']: row['ranked']
+            for row in df.to_dict(orient='records')
+        }
+        max_ts = df[DTTM_ALIAS].max()
+        max_rank = df['ranked'].max()
+        df[DTTM_ALIAS] = df.index + (max_ts - df[DTTM_ALIAS])
+        df = df.pivot_table(
+            index=DTTM_ALIAS,
+            columns='series',
+            values=fd.get('metric'))
+        chart_data = self.to_series(df)
+        for serie in chart_data:
+            serie['rank'] = rank_lookup[serie['key']]
+            serie['perc'] = 1 - (serie['rank'] / (max_rank + 1))
+        return chart_data
+
+
 class NVD3CompareTimeSeriesViz(NVD3TimeSeriesViz):
 
     """A line chart component where you can compare the % change over time"""

-- 
To stop receiving notification emails like this one, please contact
['"commits@superset.apache.org" <commits@superset.apache.org>'].

Mime
View raw message