UNPKG

3.49 kBJavaScriptView Raw
1import {Transform} from 'vega-dataflow';
2import {bin} from 'vega-statistics';
3import {accessor, accessorFields, accessorName, inherits, toNumber} from 'vega-util';
4
5// epsilon bias to offset floating point error (#1737)
6const EPSILON = 1e-14;
7
8/**
9 * Generates a binning function for discretizing data.
10 * @constructor
11 * @param {object} params - The parameters for this operator. The
12 * provided values should be valid options for the {@link bin} function.
13 * @param {function(object): *} params.field - The data field to bin.
14 */
15export default function Bin(params) {
16 Transform.call(this, null, params);
17}
18
19Bin.Definition = {
20 'type': 'Bin',
21 'metadata': {'modifies': true},
22 'params': [
23 { 'name': 'field', 'type': 'field', 'required': true },
24 { 'name': 'interval', 'type': 'boolean', 'default': true },
25 { 'name': 'anchor', 'type': 'number' },
26 { 'name': 'maxbins', 'type': 'number', 'default': 20 },
27 { 'name': 'base', 'type': 'number', 'default': 10 },
28 { 'name': 'divide', 'type': 'number', 'array': true, 'default': [5, 2] },
29 { 'name': 'extent', 'type': 'number', 'array': true, 'length': 2, 'required': true },
30 { 'name': 'span', 'type': 'number' },
31 { 'name': 'step', 'type': 'number' },
32 { 'name': 'steps', 'type': 'number', 'array': true },
33 { 'name': 'minstep', 'type': 'number', 'default': 0 },
34 { 'name': 'nice', 'type': 'boolean', 'default': true },
35 { 'name': 'name', 'type': 'string' },
36 { 'name': 'as', 'type': 'string', 'array': true, 'length': 2, 'default': ['bin0', 'bin1'] }
37 ]
38};
39
40inherits(Bin, Transform, {
41 transform(_, pulse) {
42 const band = _.interval !== false,
43 bins = this._bins(_),
44 start = bins.start,
45 step = bins.step,
46 as = _.as || ['bin0', 'bin1'],
47 b0 = as[0],
48 b1 = as[1];
49
50 let flag;
51 if (_.modified()) {
52 pulse = pulse.reflow(true);
53 flag = pulse.SOURCE;
54 } else {
55 flag = pulse.modified(accessorFields(_.field)) ? pulse.ADD_MOD : pulse.ADD;
56 }
57
58 pulse.visit(flag, band
59 ? t => {
60 const v = bins(t);
61 // minimum bin value (inclusive)
62 t[b0] = v;
63 // maximum bin value (exclusive)
64 // use convoluted math for better floating point agreement
65 // see https://github.com/vega/vega/issues/830
66 // infinite values propagate through this formula! #2227
67 t[b1] = v == null ? null : start + step * (1 + (v - start) / step);
68 }
69 : t => t[b0] = bins(t)
70 );
71
72 return pulse.modifies(band ? as : b0);
73 },
74
75 _bins(_) {
76 if (this.value && !_.modified()) {
77 return this.value;
78 }
79
80 const field = _.field,
81 bins = bin(_),
82 step = bins.step;
83 let start = bins.start,
84 stop = start + Math.ceil((bins.stop - start) / step) * step,
85 a, d;
86
87 if ((a = _.anchor) != null) {
88 d = a - (start + step * Math.floor((a - start) / step));
89 start += d;
90 stop += d;
91 }
92
93 const f = function(t) {
94 let v = toNumber(field(t));
95 return v == null ? null
96 : v < start ? -Infinity
97 : v > stop ? +Infinity
98 : (
99 v = Math.max(start, Math.min(v, stop - step)),
100 start + step * Math.floor(EPSILON + (v - start) / step)
101 );
102 };
103
104 f.start = start;
105 f.stop = bins.stop;
106 f.step = step;
107
108 return this.value = accessor(
109 f,
110 accessorFields(field),
111 _.name || 'bin_' + accessorName(field)
112 );
113 }
114});