Skip to content

Commit 5e2cd64

Browse files
committed
sketch of faster binning (1d)
1 parent 4cbad31 commit 5e2cd64

File tree

1 file changed

+63
-37
lines changed

1 file changed

+63
-37
lines changed

src/transforms/bin.js

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import {bin as binner, extent, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, utcTickInterval} from "d3";
1+
import {bisect, extent, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, ticks, utcTickInterval} from "d3";
22
import {
33
valueof,
4-
range,
54
identity,
65
maybeColumn,
76
maybeInterval,
@@ -11,7 +10,8 @@ import {
1110
mid,
1211
labelof,
1312
isTemporal,
14-
isIterable
13+
isIterable,
14+
map
1515
} from "../options.js";
1616
import {coerceDate, coerceNumber} from "../scales.js";
1717
import {basic} from "./basic.js";
@@ -147,8 +147,8 @@ function binn(
147147
const GZ = Z && setGZ([]);
148148
const GF = F && setGF([]);
149149
const GS = S && setGS([]);
150-
const BX = bx ? bx(data) : [[, , (I) => I]];
151-
const BY = by ? by(data) : [[, , (I) => I]];
150+
const BX = bx?.(data);
151+
const BY = by?.(data);
152152
const BX1 = bx && setBX1([]);
153153
const BX2 = bx && setBX2([]);
154154
const BY1 = by && setBY1([]);
@@ -164,24 +164,34 @@ function binn(
164164
if (filter) filter.scope("facet", facet);
165165
for (const [f, I] of maybeGroup(facet, G)) {
166166
for (const [k, g] of maybeGroup(I, K)) {
167-
for (const [x1, x2, fx] of BX) {
168-
const bb = fx(g);
169-
for (const [y1, y2, fy] of BY) {
170-
const extent = {x1, x2, y1, y2};
171-
const b = fy(bb);
172-
if (filter && !filter.reduce(b, extent)) continue;
173-
groupFacet.push(i++);
174-
groupData.push(reduceData.reduce(b, data, extent));
175-
if (K) GK.push(k);
176-
if (Z) GZ.push(G === Z ? f : Z[b[0]]);
177-
if (F) GF.push(G === F ? f : F[b[0]]);
178-
if (S) GS.push(G === S ? f : S[b[0]]);
179-
if (BX1) BX1.push(x1), BX2.push(x2);
180-
if (BY1) BY1.push(y1), BY2.push(y2);
181-
for (const o of outputs) o.reduce(b, extent);
182-
if (sort) sort.reduce(b);
183-
}
167+
const B = new Array(BX.length);
168+
for (const i of g) {
169+
const xi = BX.bin(i);
170+
if (B[xi]) B[xi].push(i);
171+
else B[xi] = [i];
184172
}
173+
for (const [xi, [x1, x2]] of BX.entries()) {
174+
const extent = {x1, x2, y1, y2};
175+
const b = B[xi]; //fy(bb);
176+
if (!b) continue;
177+
// if (filter && !filter.reduce(b, extent)) continue;
178+
groupFacet.push(i++);
179+
// groupData.push(reduceData.reduce(b, data, extent));
180+
groupData.push(undefined);
181+
if (K) GK.push(k);
182+
if (Z) GZ.push(G === Z ? f : Z[b[0]]);
183+
if (F) GF.push(G === F ? f : F[b[0]]);
184+
if (S) GS.push(G === S ? f : S[b[0]]);
185+
if (BX1) BX1.push(x1), BX2.push(x2);
186+
if (BY1) BY1.push(y1), BY2.push(y2);
187+
for (const o of outputs) o.reduce(b, extent);
188+
if (sort) sort.reduce(b);
189+
}
190+
// for (const [x1, x2, fx] of BX) {
191+
// const bb = fx(g);
192+
// for (const [y1, y2, fy] of BY) {
193+
// }
194+
// }
185195
}
186196
}
187197
groupFacets.push(groupFacet);
@@ -224,10 +234,12 @@ function maybeBin(options) {
224234
if (options == null) return;
225235
const {value, cumulative, domain = extent, thresholds} = options;
226236
const bin = (data) => {
227-
let V = valueof(data, value, Array); // d3.bin prefers Array input
228-
const bin = binner().value((i) => V[i]);
237+
let V = valueof(data, value);
238+
let T;
239+
// const bin = binner().value((i) => V[i]);
229240
if (isTemporal(V) || isTimeThresholds(thresholds)) {
230-
V = V.map(coerceDate);
241+
// V = V.map(coerceDate);
242+
V = map(V, coerceDate, Float64Array);
231243
let [min, max] = typeof domain === "function" ? domain(V) : domain;
232244
let t = typeof thresholds === "function" && !isInterval(thresholds) ? thresholds(V, min, max) : thresholds;
233245
if (typeof t === "number") t = utcTickInterval(min, max, t);
@@ -238,25 +250,39 @@ function maybeBin(options) {
238250
}
239251
t = t.range(min, max);
240252
}
241-
bin.thresholds(t).domain([min, max]);
253+
T = t;
242254
} else {
243-
V = V.map(coerceNumber);
244-
let d = domain;
245-
let t = thresholds;
246-
if (isInterval(t)) {
247-
let [min, max] = typeof d === "function" ? d(V) : d;
248-
if (d === extent) {
255+
// V = V.map(coerceNumber);
256+
V = map(V, coerceNumber, Float64Array);
257+
let [min, max] = typeof domain === "function" ? domain(V) : domain;
258+
let t = typeof thresholds === "function" && !isInterval(thresholds) ? thresholds(V, min, max) : thresholds;
259+
if (typeof t === "number") t = ticks(min, max, t);
260+
else if (isInterval(t)) {
261+
if (domain === extent) {
249262
min = t.floor(min);
250263
max = t.offset(t.floor(max));
251-
d = [min, max];
252264
}
253265
t = t.range(min, max);
254266
}
255-
bin.thresholds(t).domain(d);
267+
T = t;
268+
// bin.thresholds(t).domain(d);
269+
}
270+
// let bins = bin(range(data)).map(binset);
271+
// if (cumulative) bins = (cumulative < 0 ? bins.reverse() : bins).map(bincumset);
272+
// return bins.map(binfilter);
273+
const bins = [];
274+
for (let i = 0; i <= T.length; ++i) {
275+
bins.push([T[i - 1], T[i]]);
256276
}
257-
let bins = bin(range(data)).map(binset);
258-
if (cumulative) bins = (cumulative < 0 ? bins.reverse() : bins).map(bincumset);
259-
return bins.map(binfilter);
277+
// bin.thresholds(t).domain([min, max]);
278+
T = T.map(coerceNumber);
279+
// V = V.map(coerceNumber);
280+
bins.bin = (i) => {
281+
let x = V[i];
282+
if (x == null) return -1; // TODO non-comparable?
283+
return bisect(T, x, 0, T.length);
284+
};
285+
return bins;
260286
};
261287
bin.label = labelof(value);
262288
return bin;

0 commit comments

Comments
 (0)