Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 8018E200D63 for ; Thu, 7 Dec 2017 06:50:38 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 7EA03160C0A; Thu, 7 Dec 2017 05:50:38 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 76E6D160C1D for ; Thu, 7 Dec 2017 06:50:37 +0100 (CET) Received: (qmail 49589 invoked by uid 500); 7 Dec 2017 05:50:36 -0000 Mailing-List: contact commits-help@superset.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@superset.incubator.apache.org Delivered-To: mailing list commits@superset.incubator.apache.org Received: (qmail 49580 invoked by uid 99); 7 Dec 2017 05:50:36 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 07 Dec 2017 05:50:36 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id E281E82015; Thu, 7 Dec 2017 05:50:35 +0000 (UTC) Date: Thu, 07 Dec 2017 05:50:35 +0000 To: "commits@superset.apache.org" Subject: [incubator-superset] branch master updated: New time_pivot visualization (#3941) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <151262583576.10852.16220858517175726290@gitbox.apache.org> From: maximebeauchemin@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: incubator-superset X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: 5ee70b244b2751b98fefcfc18f11c6109ed7cdcd X-Git-Newrev: 5bc581fd442e0bf9308b45c51dad9e01619bcada X-Git-Rev: 5bc581fd442e0bf9308b45c51dad9e01619bcada X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated archived-at: Thu, 07 Dec 2017 05:50:38 -0000 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 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} /> {' '} 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" '].