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 <[email protected]>
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
['"[email protected]" <[email protected]>'].