forked from observablehq/plot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbin.js
93 lines (83 loc) · 2.6 KB
/
bin.js
1
2
3
4
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import {bin as binner, cross} from "d3-array";
import {valueof, first, second, maybeValue, range, offsetRange} from "../mark.js";
export function bin1(options = {}) {
let {value, domain, thresholds, cumulative} = maybeValue(options);
const bin = binof({value, domain, thresholds});
return (data, facets) => {
let bins = bin(data);
if (facets !== undefined) return binfacets(bins, facets, binsubset1, cumulative);
if (cumulative) bins = accumulate(cumulative < 0 ? bins.reverse() : bins);
bins = bins.filter(nonempty);
return {index: range(bins), data: bins};
};
}
export function bin2({x = {}, y = {}, domain, thresholds} = {}) {
const binX = binof({domain, thresholds, value: first, ...maybeValue(x)});
const binY = binof({domain, thresholds, value: second, ...maybeValue(y)});
return (data, facets) => {
let bins = cross(binX(data).filter(nonempty), binY(data).filter(nonempty).map(binset), (x, y) => {
const subbin = x.filter(i => y.has(i));
subbin.x0 = x.x0;
subbin.x1 = x.x1;
subbin.y0 = y.x0;
subbin.y1 = y.x1;
return subbin;
});
if (facets !== undefined) return binfacets(bins, facets, binsubset2);
bins = bins.filter(nonempty);
return {index: range(bins), data: bins};
};
}
function set(index) {
return new Set(index);
}
function binof({value, domain, thresholds}) {
return data => {
const values = valueof(data, value);
const bin = binner().value(i => values[i]);
if (domain !== undefined) bin.domain(domain);
if (thresholds !== undefined) bin.thresholds(thresholds);
return bin(range(values));
};
}
function binfacets(bins, facets, subset, cumulative) {
const index = [];
const data = [];
let k = 0;
for (const facet of facets.map(set)) {
let b = bins.map(bin => subset(bin, facet));
b = cumulative ? accumulate(cumulative < 0 ? b.reverse() : b) : b;
b = b.filter(nonempty);
index.push(offsetRange(b, k));
data.push(b);
k += b.length;
}
return {index, data: data.flat()};
}
function binset(bin) {
const set = new Set(bin);
set.x0 = bin.x0;
set.x1 = bin.x1;
return set;
}
function binsubset1(bin, index) {
const subbin = bin.filter(i => index.has(i));
subbin.x0 = bin.x0;
subbin.x1 = bin.x1;
return subbin;
}
function binsubset2(bin, index) {
const subbin = bin.filter(i => index.has(i));
subbin.x0 = bin.x0;
subbin.x1 = bin.x1;
subbin.y0 = bin.y0;
subbin.y1 = bin.y1;
return subbin;
}
function accumulate(bins) {
let sum = 0;
return bins.map(({x0, x1, length}) => ({x0, x1, length: sum += length}));
}
function nonempty({length}) {
return length > 0;
}