New time_pivot visualization
(cherry picked from commit 527b2c0328797de73b6bd7b3d4cc2e0a57d1b926)
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
--- /dev/null
+++ b/superset/assets/images/viz_thumbnails/time_pivot.png
Binary files 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 @@
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 @@
leftNode: PropTypes.node,
onClick: PropTypes.func,
hovered: PropTypes.bool,
+ tooltipOnClick: PropTypes.func,
};
const defaultProps = {
@@ -32,6 +33,7 @@
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 325c878..4ea4a2e 100644
--- a/superset/assets/javascripts/explore/stores/controls.jsx
+++ b/superset/assets/javascripts/explore/stores/controls.jsx
@@ -432,6 +432,29 @@
'to find in the [country] column'),
},
+ freq: {
+ type: 'SelectControl',
+ label: t('Frequency'),
+ default: '7D',
+ freeForm: true,
+ clearable: false,
+ choices: [
+ ['AS', '[AS] Year'],
+ ['52W', '[52W] 52 Weeks'],
+ ['W-SUN', '[W-SUN] Week (starting Sunday)'],
+ ['W-MON', '[W-MON] Week (starting Monday)'],
+ ['D', '[D] Day'],
+ ['4W', '[4W] 4 Weeks'],
+ ],
+ description: t(
+ 'The frequency over which to pivot time. Free-form "pandas" offset alias ' +
+ 'are allowed. Click on the info bubble for more details. '),
+ 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 ef31003..9ad1141 100644
--- a/superset/assets/javascripts/explore/stores/visTypes.js
+++ b/superset/assets/javascripts/explore/stores/visTypes.js
@@ -195,6 +195,50 @@
},
},
+ time_pivot: {
+ label: t('Time Series - Period 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 03c3bb2..f182e97 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 @@
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 @@
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 @@
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 @@
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..c37ef14 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -25,6 +25,7 @@
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 @@
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,48 @@
verbose_name = _('Time Series - Bar Chart')
+class NVD3TimePivotViz(NVD3TimeSeriesViz):
+
+ """Overlapping time series"""
+
+ 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'))
+ df = df.fillna(0)
+ 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"""