UNPKG

3.4 kBJavaScriptView Raw
1import {Transform} from 'vega-dataflow';
2import {bin} from 'vega-statistics';
3import {accessor, accessorFields, accessorName, inherits} 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
40var prototype = inherits(Bin, Transform);
41
42prototype.transform = function(_, pulse) {
43 var band = _.interval !== false,
44 bins = this._bins(_),
45 start = bins.start,
46 step = bins.step,
47 as = _.as || ['bin0', 'bin1'],
48 b0 = as[0],
49 b1 = as[1],
50 flag;
51
52 if (_.modified()) {
53 pulse = pulse.reflow(true);
54 flag = pulse.SOURCE;
55 } else {
56 flag = pulse.modified(accessorFields(_.field)) ? pulse.ADD_MOD : pulse.ADD;
57 }
58
59 pulse.visit(flag, band
60 ? function(t) {
61 var v = bins(t);
62 // minimum bin value (inclusive)
63 t[b0] = v;
64 // maximum bin value (exclusive)
65 // use convoluted math for better floating point agreement
66 // see https://github.com/vega/vega/issues/830
67 // infinite values propagate through this formula! #2227
68 t[b1] = v == null ? null : start + step * (1 + (v - start) / step);
69 }
70 : function(t) { t[b0] = bins(t); }
71 );
72
73 return pulse.modifies(band ? as : b0);
74};
75
76prototype._bins = function(_) {
77 if (this.value && !_.modified()) {
78 return this.value;
79 }
80
81 var field = _.field,
82 bins = bin(_),
83 step = bins.step,
84 start = bins.start,
85 stop = start + Math.ceil((bins.stop - start) / step) * step,
86 a, d;
87
88 if ((a = _.anchor) != null) {
89 d = a - (start + step * Math.floor((a - start) / step));
90 start += d;
91 stop += d;
92 }
93
94 var f = function(t) {
95 var v = field(t);
96 return v == null ? null
97 : v < start ? -Infinity
98 : v > stop ? +Infinity
99 : (
100 v = Math.max(start, Math.min(+v, stop - step)),
101 start + step * Math.floor(EPSILON + (v - start) / step)
102 );
103 };
104
105 f.start = start;
106 f.stop = bins.stop;
107 f.step = step;
108
109 return this.value = accessor(
110 f,
111 accessorFields(field),
112 _.name || 'bin_' + accessorName(field)
113 );
114};