UNPKG

vega-transforms

Version:

Data processing transforms for Vega dataflows.

74 lines (65 loc) 2.45 kB
import {partition} from './util/util.js'; import {Transform, stableCompare} from 'vega-dataflow'; import {dotbin} from 'vega-statistics'; import {extent, identity, inherits, span} from 'vega-util'; const Output = 'bin'; /** * Dot density binning for dot plot construction. * Based on Leland Wilkinson, Dot Plots, The American Statistician, 1999. * https://www.cs.uic.edu/~wilkinson/Publications/dotplots.pdf * @constructor * @param {object} params - The parameters for this operator. * @param {function(object): *} params.field - The value field to bin. * @param {Array<function(object): *>} [params.groupby] - An array of accessors to groupby. * @param {number} [params.step] - The step size (bin width) within which dots should be * stacked. Defaults to 1/30 of the extent of the data *field*. * @param {boolean} [params.smooth=false] - A boolean flag indicating if dot density * stacks should be smoothed to reduce variance. */ export default function DotBin(params) { Transform.call(this, null, params); } DotBin.Definition = { 'type': 'DotBin', 'metadata': {'modifies': true}, 'params': [ { 'name': 'field', 'type': 'field', 'required': true }, { 'name': 'groupby', 'type': 'field', 'array': true }, { 'name': 'step', 'type': 'number' }, { 'name': 'smooth', 'type': 'boolean', 'default': false }, { 'name': 'as', 'type': 'string', 'default': Output } ] }; const autostep = (data, field) => span(extent(data, field)) / 30; inherits(DotBin, Transform, { transform(_, pulse) { if (this.value && !(_.modified() || pulse.changed())) { return pulse; // early exit } const source = pulse.materialize(pulse.SOURCE).source, groups = partition(pulse.source, _.groupby, identity), smooth = _.smooth || false, field = _.field, step = _.step || autostep(source, field), sort = stableCompare((a, b) => field(a) - field(b)), as = _.as || Output, n = groups.length; // compute dotplot bins per group let min = Infinity, max = -Infinity, i = 0, j; for (; i<n; ++i) { const g = groups[i].sort(sort); j = -1; for (const v of dotbin(g, step, smooth, field)) { if (v < min) min = v; if (v > max) max = v; g[++j][as] = v; } } this.value = { start: min, stop: max, step: step }; return pulse.reflow(true).modifies(as); } });