1 | import {partition} from './util/util';
|
2 | import {randomKDE} from 'vega-statistics';
|
3 | import {Transform, ingest} from 'vega-dataflow';
|
4 | import {sampleCurve} from 'vega-statistics';
|
5 | import {accessorName, error, extent, inherits} from 'vega-util';
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | export default function KDE(params) {
|
39 | Transform.call(this, null, params);
|
40 | }
|
41 |
|
42 | KDE.Definition = {
|
43 | 'type': 'KDE',
|
44 | 'metadata': {'generates': true},
|
45 | 'params': [
|
46 | { 'name': 'groupby', 'type': 'field', 'array': true },
|
47 | { 'name': 'field', 'type': 'field', 'required': true },
|
48 | { 'name': 'cumulative', 'type': 'boolean', 'default': false },
|
49 | { 'name': 'counts', 'type': 'boolean', 'default': false },
|
50 | { 'name': 'bandwidth', 'type': 'number', 'default': 0 },
|
51 | { 'name': 'extent', 'type': 'number', 'array': true, 'length': 2 },
|
52 | { 'name': 'resolve', 'type': 'enum', 'values': ['shared', 'independent'], 'default': 'independent' },
|
53 | { 'name': 'steps', 'type': 'number' },
|
54 | { 'name': 'minsteps', 'type': 'number', 'default': 25 },
|
55 | { 'name': 'maxsteps', 'type': 'number', 'default': 200 },
|
56 | { 'name': 'as', 'type': 'string', 'array': true, 'default': ['value', 'density'] }
|
57 | ]
|
58 | };
|
59 |
|
60 | inherits(KDE, Transform, {
|
61 | transform(_, pulse) {
|
62 | const out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
|
63 |
|
64 | if (!this.value || pulse.changed() || _.modified()) {
|
65 | const source = pulse.materialize(pulse.SOURCE).source,
|
66 | groups = partition(source, _.groupby, _.field),
|
67 | names = (_.groupby || []).map(accessorName),
|
68 | bandwidth = _.bandwidth,
|
69 | method = _.cumulative ? 'cdf' : 'pdf',
|
70 | as = _.as || ['value', 'density'],
|
71 | values = [];
|
72 |
|
73 | let domain = _.extent,
|
74 | minsteps = _.steps || _.minsteps || 25,
|
75 | maxsteps = _.steps || _.maxsteps || 200;
|
76 |
|
77 | if (method !== 'pdf' && method !== 'cdf') {
|
78 | error('Invalid density method: ' + method);
|
79 | }
|
80 |
|
81 | if (_.resolve === 'shared') {
|
82 | if (!domain) domain = extent(source, _.field);
|
83 | minsteps = maxsteps = _.steps || maxsteps;
|
84 | }
|
85 |
|
86 | groups.forEach(g => {
|
87 | const density = randomKDE(g, bandwidth)[method],
|
88 | scale = _.counts ? g.length : 1,
|
89 | local = domain || extent(g);
|
90 |
|
91 | sampleCurve(density, local, minsteps, maxsteps)
|
92 | .forEach(v => {
|
93 | const t = {};
|
94 | for (let i=0; i<names.length; ++i) {
|
95 | t[names[i]] = g.dims[i];
|
96 | }
|
97 | t[as[0]] = v[0];
|
98 | t[as[1]] = v[1] * scale;
|
99 | values.push(ingest(t));
|
100 | });
|
101 | });
|
102 |
|
103 | if (this.value) out.rem = this.value;
|
104 | this.value = out.add = out.source = values;
|
105 | }
|
106 |
|
107 | return out;
|
108 | }
|
109 | });
|