1 | import parseDist from './util/Distributions';
|
2 | import {Transform, ingest} from 'vega-dataflow';
|
3 | import {sampleCurve} from 'vega-statistics';
|
4 | import {error, extent, inherits} from 'vega-util';
|
5 |
|
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 | export default function Density(params) {
|
31 | Transform.call(this, null, params);
|
32 | }
|
33 |
|
34 | var distributions = [
|
35 | {
|
36 | 'key': {'function': 'normal'},
|
37 | 'params': [
|
38 | { 'name': 'mean', 'type': 'number', 'default': 0 },
|
39 | { 'name': 'stdev', 'type': 'number', 'default': 1 }
|
40 | ]
|
41 | },
|
42 | {
|
43 | 'key': {'function': 'lognormal'},
|
44 | 'params': [
|
45 | { 'name': 'mean', 'type': 'number', 'default': 0 },
|
46 | { 'name': 'stdev', 'type': 'number', 'default': 1 }
|
47 | ]
|
48 | },
|
49 | {
|
50 | 'key': {'function': 'uniform'},
|
51 | 'params': [
|
52 | { 'name': 'min', 'type': 'number', 'default': 0 },
|
53 | { 'name': 'max', 'type': 'number', 'default': 1 }
|
54 | ]
|
55 | },
|
56 | {
|
57 | 'key': {'function': 'kde'},
|
58 | 'params': [
|
59 | { 'name': 'field', 'type': 'field', 'required': true },
|
60 | { 'name': 'from', 'type': 'data' },
|
61 | { 'name': 'bandwidth', 'type': 'number', 'default': 0 }
|
62 | ]
|
63 | }
|
64 | ];
|
65 |
|
66 | var mixture = {
|
67 | 'key': {'function': 'mixture'},
|
68 | 'params': [
|
69 | { 'name': 'distributions', 'type': 'param', 'array': true,
|
70 | 'params': distributions },
|
71 | { 'name': 'weights', 'type': 'number', 'array': true }
|
72 | ]
|
73 | };
|
74 |
|
75 | Density.Definition = {
|
76 | 'type': 'Density',
|
77 | 'metadata': {'generates': true},
|
78 | 'params': [
|
79 | { 'name': 'extent', 'type': 'number', 'array': true, 'length': 2 },
|
80 | { 'name': 'steps', 'type': 'number' },
|
81 | { 'name': 'minsteps', 'type': 'number', 'default': 25 },
|
82 | { 'name': 'maxsteps', 'type': 'number', 'default': 200 },
|
83 | { 'name': 'method', 'type': 'string', 'default': 'pdf',
|
84 | 'values': ['pdf', 'cdf'] },
|
85 | { 'name': 'distribution', 'type': 'param',
|
86 | 'params': distributions.concat(mixture) },
|
87 | { 'name': 'as', 'type': 'string', 'array': true,
|
88 | 'default': ['value', 'density'] }
|
89 | ]
|
90 | };
|
91 |
|
92 | var prototype = inherits(Density, Transform);
|
93 |
|
94 | prototype.transform = function(_, pulse) {
|
95 | var out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
|
96 |
|
97 | if (!this.value || pulse.changed() || _.modified()) {
|
98 | var dist = parseDist(_.distribution, source(pulse)),
|
99 | minsteps = _.steps || _.minsteps || 25,
|
100 | maxsteps = _.steps || _.maxsteps || 200,
|
101 | method = _.method || 'pdf';
|
102 |
|
103 | if (method !== 'pdf' && method !== 'cdf') {
|
104 | error('Invalid density method: ' + method);
|
105 | }
|
106 | if (!_.extent && !dist.data) {
|
107 | error('Missing density extent parameter.');
|
108 | }
|
109 | method = dist[method];
|
110 |
|
111 | var as = _.as || ['value', 'density'],
|
112 | domain = _.extent || extent(dist.data()),
|
113 | values = sampleCurve(method, domain, minsteps, maxsteps).map(v => {
|
114 | var tuple = {};
|
115 | tuple[as[0]] = v[0];
|
116 | tuple[as[1]] = v[1];
|
117 | return ingest(tuple);
|
118 | });
|
119 |
|
120 | if (this.value) out.rem = this.value;
|
121 | this.value = out.add = out.source = values;
|
122 | }
|
123 |
|
124 | return out;
|
125 | };
|
126 |
|
127 | function source(pulse) {
|
128 | return function() { return pulse.materialize(pulse.SOURCE).source; };
|
129 | }
|