Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1D charts generated from columnar data #231

Merged
merged 5 commits into from
Sep 27, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Optimize make_xy_column_data, now consistently achieves 2-6x speedup …
…over row-based series generation
  • Loading branch information
sc1f committed Sep 26, 2018
commit 08ae7cd669b081570245ff64c70dc57980b0ceb0
140 changes: 29 additions & 111 deletions packages/perspective-viewer-highcharts/src/js/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
*/

import Highcharts from "highcharts";
import Highcharts from 'highcharts';

import "../less/highcharts.less";

Expand All @@ -16,12 +16,11 @@ import {color_axis} from "./color_axis.js";
import {make_tree_data, make_y_data, make_xy_data, make_xyz_data, make_xy_column_data} from "./series.js";
import {set_boost, set_category_axis, set_both_axis, default_config, set_tick_size} from "./config.js";

export const draw = mode =>
async function(el, view, task) {
const row_pivots = this._view_columns("#row_pivots perspective-row:not(.off)");
const col_pivots = this._view_columns("#column_pivots perspective-row:not(.off)");
const aggregates = this._get_view_aggregates();
const hidden = this._get_view_hidden(aggregates);
export const draw = (mode) => async function (el, view, task) {
const row_pivots = this._view_columns('#row_pivots perspective-row:not(.off)');
const col_pivots = this._view_columns('#column_pivots perspective-row:not(.off)');
const aggregates = this._get_view_aggregates();
const hidden = this._get_view_hidden(aggregates);

const [schema, tschema] = await Promise.all([view.schema(), this._table.schema()]);

Expand All @@ -31,9 +30,9 @@ export const draw = mode =>
return;
}

if (!this._charts) {
this._charts = [];
}
if (!this._charts) {
this._charts = [];
}

let configs = [],
xaxis_name = aggregates.length > 0 ? aggregates[0].column : undefined,
Expand All @@ -47,22 +46,9 @@ export const draw = mode =>
num_aggregates = aggregates.length - hidden.length;

if (mode === 'scatter') {
let s;
let config = configs[0] = default_config.call(this, aggregates, mode);

// determine whether to use column/row data
if (col_pivots.length === 0) {
const cols = await view.to_columns();
s = await make_xy_column_data(cols, schema, aggregates.map(x => x.column), row_pivots, col_pivots, hidden);
} else {
js = await view.to_json();
s = await make_xy_data(js, schema, aggregates.map(x => x.column), row_pivots, col_pivots, hidden);
}

const series = s[0];
const xtop = s[1];
const colorRange = s[2];
const ytop = s[3];
const cols = await view.to_columns();
const [series, xtop, colorRange, ytop] = make_xy_column_data(cols, schema, aggregates.map(x => x.column), row_pivots, col_pivots, hidden);
const config = configs[0] = default_config.call(this, aggregates, mode);

config.legend.floating = series.length <= 20;
config.legend.enabled = col_pivots.length > 0;
Expand All @@ -74,81 +60,10 @@ export const draw = mode =>
} else {
config.chart.type = 'coloredBubble';
}
if (num_aggregates < 3) {
set_boost(config, xaxis_type, yaxis_type);
}
set_both_axis(config, "xAxis", xaxis_name, xaxis_type, xaxis_type, xtop);
set_both_axis(config, "yAxis", yaxis_name, yaxis_type, yaxis_type, ytop);
set_tick_size.call(this, config);
} else if (mode === "heatmap") {
let config = (configs[0] = default_config.call(this, aggregates, mode, js, col_pivots));
let [series, top, ytop, colorRange] = make_xyz_data(js, row_pivots, hidden, ytree_type);
config.series = [
{
name: null,
data: series,
nullColor: "none"
}
];
config.legend.enabled = true;
config.legend.floating = false;

color_axis.call(this, config, colorRange);
}
if (num_aggregates < 3) {
set_boost(config, xaxis_type, yaxis_type);
set_category_axis(config, "xAxis", xtree_type, top);
set_category_axis(config, "yAxis", ytree_type, ytop);
} else if (mode === "treemap" || mode === "sunburst") {
let [charts, , colorRange] = make_tree_data(js, row_pivots, hidden, aggregates, mode === "treemap");
for (let series of charts) {
let config = default_config.call(this, aggregates, mode, js, col_pivots);
config.series = [series];
if (charts.length > 1) {
config.title.text = series.title;
}
config.plotOptions.series.borderWidth = 1;
config.legend.floating = false;
if (colorRange) {
color_axis.call(this, config, colorRange);
}
configs.push(config);
}
} else if (mode === "line") {
let config = (configs[0] = default_config.call(this, aggregates, mode, js, col_pivots));
let [series, xtop, , ytop] = make_xy_data(js, schema, aggregates.map(x => x.column), row_pivots, col_pivots, hidden);
const colors = series.length <= 10 ? COLORS_10 : COLORS_20;
config.legend.floating = series.length <= 20;
config.legend.enabled = col_pivots.length > 0;
config.series = series;
config.plotOptions.scatter.marker = {enabled: false, radius: 0};
config.colors = colors;
if (set_boost(config, xaxis_type, yaxis_type)) {
delete config.chart["type"];
}
set_both_axis(config, "xAxis", xaxis_name, xaxis_type, xaxis_type, xtop);
set_both_axis(config, "yAxis", yaxis_name, yaxis_type, yaxis_type, ytop);
} else {
let config = (configs[0] = default_config.call(this, aggregates, mode, js, col_pivots));
let [series, top] = make_y_data(js, row_pivots, hidden);
config.series = series;
config.colors = series.length <= 10 ? COLORS_10 : COLORS_20;
config.legend.enabled = col_pivots.length > 0 || series.length > 1;
config.legend.floating = series.length <= 20;
config.plotOptions.series.dataLabels = {
allowOverlap: false,
padding: 10
};
set_category_axis(config, "xAxis", xtree_type, top);
Object.assign(config, {
yAxis: {
startOnTick: false,
endOnTick: false,
title: {
text: aggregates.map(x => x.column).join(", "),
style: {color: "#666666", fontSize: "14px"}
},
labels: {overflow: "justify"}
}
});
}
set_both_axis(config, 'xAxis', xaxis_name, xaxis_type, xaxis_type, xtop);
set_both_axis(config, 'yAxis', yaxis_name, yaxis_type, yaxis_type, ytop);
Expand Down Expand Up @@ -179,10 +94,10 @@ export const draw = mode =>
if (charts.length > 1) {
config.title.text = series.title;
}
} else {
this._charts = [];
for (let e of Array.prototype.slice.call(el.children)) {
el.removeChild(e);
config.plotOptions.series.borderWidth = 1;
config.legend.floating = false;
if (colorRange) {
color_axis.call(this, config, colorRange);
}
configs.push(config);
}
Expand Down Expand Up @@ -236,6 +151,8 @@ export const draw = mode =>
},
labels: {overflow: 'justify'}
}
});
}

if (this.hasAttribute('updating') && this._charts.length > 0) {
for (let chart of this._charts) {
Expand Down Expand Up @@ -267,14 +184,16 @@ export const draw = mode =>
let opts = {series: config.series, xAxis: config.xAxis, yAxis: config.yAxis};
chart.update(opts);
}
this._charts.map(x => el.appendChild(x.renderTo));
}

// TODO resize bug in Highcharts?
if (configs.length > 1) {
this._charts.map(x => x.reflow());
} else {
this._charts = [];
for (let e of Array.prototype.slice.call(el.children)) { el.removeChild(e); }
for (let config of configs) {
let chart = document.createElement('div');
chart.className = 'chart';
el.appendChild(chart);
this._charts.push(() => Highcharts.chart(chart, config));
}
};

for (let i = 0; i < this._charts.length; i++) {
this._charts[i] = this._charts[i]();
Expand All @@ -291,5 +210,4 @@ export const draw = mode =>
if (configs.length > 1) {
this._charts.map(x => x.reflow());
}
}

}
43 changes: 20 additions & 23 deletions packages/perspective-viewer-highcharts/src/js/series.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ class MakeTick {
}

if (cols.length === undefined) {
// Dealing with a ColumnIterator object - must map data to 2D array properly
// assign data to array in name order
data = [];
for (let name of col_names) {
data.push(cols[name]);
Expand All @@ -342,7 +342,6 @@ class MakeTick {

for (let i = 0; i < data[0].length; i++) {
if(data[0][i] === null || data[0][i] === undefined || data[0][i] === "") {
data[0][i] = null;
continue;
}

Expand All @@ -357,13 +356,11 @@ class MakeTick {
}

// set x-axis
tick.x = data[0][i];
tick.x = this.xaxis_clean.clean(tick.x);
tick.x = this.xaxis_clean.clean(data[0][i]);

if (num_cols > 1) {
// set y-axis
tick.y = data[1][i];
tick.y = this.yaxis_clean.clean(tick.y);
tick.y = this.yaxis_clean.clean(data[1][i]);
}

if (num_cols > 2) {
Expand Down Expand Up @@ -397,7 +394,7 @@ class MakeTick {
}
}

export async function make_xy_column_data(cols, schema, aggs, pivots, col_pivots, hidden) {
export function make_xy_column_data(cols, schema, aggs, pivots, col_pivots, hidden) {
const columns = new ColumnIterator(cols, hidden, pivots.length);
let series = [];
let color_range = [Infinity, -Infinity];
Expand Down Expand Up @@ -442,28 +439,28 @@ export async function make_xy_column_data(cols, schema, aggs, pivots, col_pivots
}

groups[group_name].push(col.data);
}

// FIXME: this is the heaviest loop
for (let name in groups) {
let ticks = make_tick.make_col(
groups[name],
aggs,
aggs.length,
columns.pivot_length,
row_path,
color_range,
);

let s = column_to_series(ticks, name);
series.push(s);
if (groups[group_name].length === aggs.length) {
// generate series as soon as we have enough data
let ticks = make_tick.make_col(
groups[group_name],
aggs,
aggs.length,
columns.pivot_length,
row_path,
color_range,
);

let s = column_to_series(ticks, group_name);
series.push(s);
}
}
}

return [series, {categories: make_tick.xaxis_clean.names}, color_range, {categories: make_tick.yaxis_clean.names}];
}

export async function make_xy_data(js, schema, columns, pivots, col_pivots, hidden) {
export function make_xy_data(js, schema, columns, pivots, col_pivots, hidden) {
let rows = new TreeAxisIterator(pivots.length, js);
let rows2 = new RowIterator(rows, hidden);
let series = [];
Expand Down Expand Up @@ -514,7 +511,7 @@ export async function make_xy_data(js, schema, columns, pivots, col_pivots, hidd
}
}
}

return [series, {categories: make_tick.xaxis_clean.names}, colorRange, {categories: make_tick.yaxis_clean.names}];
}

Expand Down