Skip to content

Commit

Permalink
[WIP] Add dual-axis line chart to viz (#1782)
Browse files Browse the repository at this point in the history
* Add dual-axis line chart to viz
 - Add a new viz using two y axis for multi-chart in nvd3
 - Option to set metric and axis format for two y axis

* Resolve conflicts

* Fixed x axis and resized thumbnail

* Added example to __init__.py

* Change get_df to match with format
  • Loading branch information
vera-liu authored Jan 6, 2017
1 parent 119b0c5 commit c3edc6e
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 10 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export const exploreReducer = function (state, action) {
newState.filterColumnOpts = optionsByFieldName[fieldName];
} else {
newState.fields[fieldName].choices = optionsByFieldName[fieldName];
if (fieldName === 'metric' && state.viz.form_data.viz_type === 'dual_line') {
newState.fields.metric_2.choices = optionsByFieldName[fieldName];
}
}
});
return Object.assign({}, state, newState);
Expand Down
16 changes: 16 additions & 0 deletions superset/assets/javascripts/explorev2/stores/fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ export const fields = {
description: 'Choose the metric',
},

metric_2: {
type: 'SelectField',
label: 'Right Axis Metric',
choices: [],
default: [],
description: 'Choose a metric for right axis',
},

stacked_style: {
type: 'SelectField',
label: 'Stacked Style',
Expand Down Expand Up @@ -692,6 +700,14 @@ export const fields = {
description: D3_FORMAT_DOCS,
},

y_axis_2_format: {
type: 'FreeFormSelectField',
label: 'Right axis format',
default: '.3s',
choices: D3_TIME_FORMAT_OPTIONS,
description: D3_FORMAT_DOCS,
},

markup_type: {
type: 'SelectField',
label: 'Markup Type',
Expand Down
36 changes: 36 additions & 0 deletions superset/assets/javascripts/explorev2/stores/visTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,42 @@ const visTypes = {
],
},

dual_line: {
label: 'Time Series - Dual Axis Line Chart',
requiresTime: true,
controlPanelSections: [
{
label: 'Chart Options',
fieldSetRows: [
['x_axis_format'],
],
},
{
label: 'Y Axis 1',
fieldSetRows: [
['metric'],
['y_axis_format'],
],
},
{
label: 'Y Axis 2',
fieldSetRows: [
['metric_2'],
['y_axis_2_format'],
],
},
],
fieldOverrides: {
metric: {
label: 'Left Axis Metric',
description: 'Choose a metric for left axis',
},
y_axis_format: {
label: 'Left Axis Format',
},
},
},

bar: {
label: 'Time Series - Bar Chart',
requiresTime: true,
Expand Down
1 change: 1 addition & 0 deletions superset/assets/visualizations/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ const vizMap = {
treemap: require('./treemap.js'),
word_cloud: require('./word_cloud.js'),
world_map: require('./world_map.js'),
dual_line: require('./nvd3_vis.js'),
};
export default vizMap;
14 changes: 12 additions & 2 deletions superset/assets/visualizations/nvd3_vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ function nvd3Vis(slice) {
.staggerLabels(false);
break;

case 'dual_line':
chart = nv.models.multiChart();
chart.interpolate('linear');
break;

case 'bar':
chart = nv.models.multiBarChart()
.showControls(fd.show_controls)
Expand Down Expand Up @@ -309,7 +314,7 @@ function nvd3Vis(slice) {
chart.yAxis.tickFormat(d3.format('.3s'));
}

if (fd.y_axis_format) {
if (fd.y_axis_format && chart.yAxis) {
chart.yAxis.tickFormat(d3.format(fd.y_axis_format));
if (chart.y2Axis !== undefined) {
chart.y2Axis.tickFormat(d3.format(fd.y_axis_format));
Expand Down Expand Up @@ -347,7 +352,12 @@ function nvd3Vis(slice) {
if (svg.empty()) {
svg = d3.select(slice.selector).append('svg');
}

if (vizType === 'dual_line') {
chart.yAxis1.tickFormat(d3.format(fd.y_axis_format));
chart.yAxis2.tickFormat(d3.format(fd.y_axis_2_format));
chart.showLegend(true);
chart.margin({ right: 50 });
}
svg
.datum(payload.data)
.transition().duration(500)
Expand Down
9 changes: 9 additions & 0 deletions superset/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,15 @@ def load_birth_names():
defaults,
viz_type="line", groupby=['name'],
granularity='ds', rich_tooltip='y', show_legend='y')),
Slice(
slice_name="Average and Sum Trends",
viz_type='dual_line',
datasource_type='table',
datasource_id=tbl.id,
params=get_slice_json(
defaults,
viz_type="dual_line", metric='avg__num', metric_2='sum__num',
granularity='ds')),
Slice(
slice_name="Title",
viz_type='markup',
Expand Down
29 changes: 21 additions & 8 deletions superset/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
'"%Y-%m-%d %H:%M:%S" | 2019-01-14 01:32:10'),
("%H:%M:%S", '"%H:%M:%S" | 01:32:10'),
]
AXIS_FORMAT_CHOICES = [
('.3s', '".3s" | 12.3k'),
('.3%', '".3%" | 1234543.210%'),
('.4r', '".4r" | 12350'),
('.3f', '".3f" | 12345.432'),
('+,', '"+," | +12,345.4321'),
('$,.2f', '"$,.2f" | $12,345.43'),
]
D3_FORMAT_DOCS = _(
"D3 format syntax "
"https://github.com/d3/d3-format")
Expand Down Expand Up @@ -162,6 +170,12 @@ def __init__(self, viz):
"default": default_metric,
"description": _("Choose the metric")
}),
'metric_2': (SelectField, {
"label": _("Right Axis Metric"),
"choices": datasource.metrics_combo,
"default": default_metric,
"description": _("Choose the metric for second y axis")
}),
'stacked_style': (SelectField, {
"label": _("Chart Style"),
"choices": (
Expand Down Expand Up @@ -679,14 +693,13 @@ def __init__(self, viz):
'y_axis_format': (FreeFormSelectField, {
"label": _("Y axis format"),
"default": '.3s',
"choices": [
('.3s', '".3s" | 12.3k'),
('.3%', '".3%" | 1234543.210%'),
('.4r', '".4r" | 12350'),
('.3f', '".3f" | 12345.432'),
('+,', '"+," | +12,345.4321'),
('$,.2f', '"$,.2f" | $12,345.43'),
],
"choices": AXIS_FORMAT_CHOICES,
"description": D3_FORMAT_DOCS,
}),
'y_axis_2_format': (FreeFormSelectField, {
"label": _("Right axis format"),
"default": '.3s',
"choices": AXIS_FORMAT_CHOICES,
"description": D3_FORMAT_DOCS,
}),
'markup_type': (SelectField, {
Expand Down
108 changes: 108 additions & 0 deletions superset/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,8 @@ def to_series(self, df, classed='', title_suffix=''):

def get_data(self):
df = self.get_df()
import ipdb
ipdb.set_trace()
chart_data = self.to_series(df)

time_compare = self.form_data.get('time_compare')
Expand All @@ -1272,6 +1274,111 @@ def get_data(self):
return chart_data


class NVD3DualLineViz(NVD3Viz):

"""A rich line chart with dual axis"""

viz_type = "dual_line"
verbose_name = _("Time Series - Dual Axis Line Chart")
sort_series = False
is_timeseries = True
fieldsets = ({
'label': _('Chart Options'),
'fields': ('x_axis_format',),
}, {
'label': _('Y Axis 1'),
'fields': (
'metric',
'y_axis_format'
),
}, {
'label': _('Y Axis 2'),
'fields': (
'metric_2',
'y_axis_2_format'
),
},)
form_overrides = {
'y_axis_format': {
'label': _('Left Axis Format'),
'description': _("Select the numeric column to draw the histogram"),
},
'metric': {
'label': _("Left Axis Metric"),
}
}

def get_df(self, query_obj=None):
if not query_obj:
query_obj = super(NVD3DualLineViz, self).query_obj()
metrics = [
self.form_data.get('metric'),
self.form_data.get('metric_2')
]
query_obj['metrics'] = metrics
df = super(NVD3DualLineViz, self).get_df(query_obj)
df = df.fillna(0)
if self.form_data.get("granularity") == "all":
raise Exception("Pick a time granularity for your time series")

df = df.pivot_table(
index=DTTM_ALIAS,
values=metrics)

return df

def to_series(self, df, classed=''):
cols = []
for col in df.columns:
if col == '':
cols.append('N/A')
elif col is None:
cols.append('NULL')
else:
cols.append(col)
df.columns = cols
series = df.to_dict('series')
chart_data = []
index_list = df.T.index.tolist()
for i in range(0, len(index_list)):
name = index_list[i]
ys = series[name]
if df[name].dtype.kind not in "biufc":
continue
df[DTTM_ALIAS] = pd.to_datetime(df.index, utc=False)
if isinstance(name, string_types):
series_title = name
else:
name = ["{}".format(s) for s in name]
series_title = ", ".join(name[1:])

d = {
"key": series_title,
"classed": classed,
"values": [
{'x': ds, 'y': ys[ds] if ds in ys else None}
for ds in df[DTTM_ALIAS]
],
"yAxis": i+1,
"type": "line"
}
chart_data.append(d)
return chart_data

def get_data(self):
form_data = self.form_data
metric = form_data.get('metric')
metric_2 = form_data.get('metric_2')
if not metric:
raise Exception("Pick a metric for left axis!")
if not metric_2:
raise Exception("Pick a metric for right axis!")

df = self.get_df()
chart_data = self.to_series(df)
return chart_data


class NVD3TimeSeriesBarViz(NVD3TimeSeriesViz):

"""A bar chart where the x axis is time"""
Expand Down Expand Up @@ -2102,6 +2209,7 @@ def get_data(self):
TableViz,
PivotTableViz,
NVD3TimeSeriesViz,
NVD3DualLineViz,
NVD3CompareTimeSeriesViz,
NVD3TimeSeriesStackedViz,
NVD3TimeSeriesBarViz,
Expand Down

0 comments on commit c3edc6e

Please sign in to comment.