Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions src/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default function bin() {
var i,
n = data.length,
x,
step,
values = new Array(n);

for (i = 0; i < n; ++i) {
Expand All @@ -36,6 +37,11 @@ export default function bin() {
if (domain === extent) [x0, x1] = nice(x0, x1, tn);
tz = ticks(x0, x1, tn);

// If the domain is aligned with the first tick (which it will by
// default), then we can use quantization rather than bisection to bin
// values, which is substantially faster.
if (tz[0] <= x0) step = tickIncrement(x0, x1, tn);

// If the last threshold is coincident with the domain’s upper bound, the
// last bin will be zero-width. If the default domain is used, and this
// last threshold is coincident with the maximum input value, we can
Expand Down Expand Up @@ -75,10 +81,25 @@ export default function bin() {
}

// Assign data to bins by value, ignoring any outside the domain.
for (i = 0; i < n; ++i) {
x = values[i];
if (x != null && x0 <= x && x <= x1) {
bins[bisect(tz, x, 0, m)].push(data[i]);
if (isFinite(step)) {
if (step > 0) {
for (i = 0; i < n; ++i) {
if ((x = values[i]) != null && x0 <= x && x <= x1) {
bins[Math.floor((x - x0) / step)].push(data[i]);
}
}
} else if (step < 0) {
for (i = 0; i < n; ++i) {
if ((x = values[i]) != null && x0 <= x && x <= x1) {
bins[Math.floor((x0 - x) * step)].push(data[i]);
}
}
}
} else {
for (i = 0; i < n; ++i) {
if ((x = values[i]) != null && x0 <= x && x <= x1) {
bins[bisect(tz, x, 0, m)].push(data[i]);
}
}
}

Expand Down
42 changes: 42 additions & 0 deletions test/bin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,48 @@ it("bin()() returns bins whose rightmost bin is not too wide", () => {
]);
});

it("bin(data) handles fractional step correctly", () => {
const h = bin().thresholds(10);
assert.deepStrictEqual(h([9.8, 10, 11, 12, 13, 13.2]), [
box([9.8], 9.5, 10),
box([10], 10, 10.5),
box([], 10.5, 11),
box([11], 11, 11.5),
box([], 11.5, 12),
box([12], 12, 12.5),
box([], 12.5, 13),
box([13, 13.2], 13, 13.5)
]);
});

it("bin(data) handles fractional step correctly with a custom, non-aligned domain", () => {
const h = bin().thresholds(10).domain([9.7, 13.3]);
assert.deepStrictEqual(h([9.8, 10, 11, 12, 13, 13.2]), [
box([9.8], 9.7, 10),
box([10], 10, 10.5),
box([], 10.5, 11),
box([11], 11, 11.5),
box([], 11.5, 12),
box([12], 12, 12.5),
box([], 12.5, 13),
box([13, 13.2], 13, 13.3)
]);
});

it("bin(data) handles fractional step correctly with a custom, aligned domain", () => {
const h = bin().thresholds(10).domain([9.5, 13.5]);
assert.deepStrictEqual(h([9.8, 10, 11, 12, 13, 13.2]), [
box([9.8], 9.5, 10),
box([10], 10, 10.5),
box([], 10.5, 11),
box([11], 11, 11.5),
box([], 11.5, 12),
box([12], 12, 12.5),
box([], 12.5, 13),
box([13, 13.2], 13, 13.5)
]);
});

it("bin(data) coerces values to numbers as expected", () => {
const h = bin().thresholds(10);
assert.deepStrictEqual(h(["1", "2", "3", "4", "5"]), [
Expand Down