Skip to content

Commit

Permalink
Fix zero-width last bin.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Sep 26, 2020
1 parent a947cba commit 1a23ff0
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 17 deletions.
33 changes: 26 additions & 7 deletions src/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import constant from "./constant.js";
import extent from "./extent.js";
import identity from "./identity.js";
import nice from "./nice.js";
import ticks from "./ticks.js";
import ticks, {tickIncrement} from "./ticks.js";
import sturges from "./threshold/sturges.js";

export default function() {
Expand All @@ -29,13 +29,32 @@ export default function() {
x1 = xz[1],
tz = threshold(values, x0, x1);

// Convert number of thresholds into uniform thresholds,
// and nice the default domain accordingly.
// Convert number of thresholds into uniform thresholds, and nice the
// default domain accordingly.
if (!Array.isArray(tz)) {
tz = +tz;
if (domain === extent) [x0, x1] = nice(x0, x1, tz);
tz = ticks(x0, x1, tz);
if (tz[tz.length - 1] === x1) tz.pop(); // exclusive
const max = x1, tn = +tz;
if (domain === extent) [x0, x1] = nice(x0, x1, tn);
tz = ticks(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
// extend the niced upper bound by one tick to ensure uniform bin widths;
// otherwise, we simply remove the last threshold.
if (tz[tz.length - 1] === x1) {
if (x1 === max && domain === extent) {
const step = tickIncrement(x0, x1, tn);
if (isFinite(step)) {
if (step > 0) {
x1 = (Math.floor(x1 / step) + 1) * step;
} else if (step < 0) {
x1 = (Math.ceil(x1 * step) + 1) / step;
}
}
} else {
tz.pop();
}
}
}

// Remove any thresholds outside the domain.
Expand Down
25 changes: 15 additions & 10 deletions test/bin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ tape("bin(data) computes bins of the specified array of data", (test) => {
bin([0, 0, 0], 0, 5),
bin([], 5, 10),
bin([10], 10, 15),
bin([20, 20], 15, 20) // Note: inclusive upper bound for last bin.
bin([], 15, 20),
bin([20, 20], 20, 25)
]);
});

Expand All @@ -28,7 +29,8 @@ tape("bin(iterable) is equivalent to bin(array)", (test) => {
bin([0, 0, 0], 0, 5),
bin([], 5, 10),
bin([10], 10, 15),
bin([20, 20], 15, 20) // Note: inclusive upper bound for last bin.
bin([], 15, 20),
bin([20, 20], 20, 25)
]);
});

Expand All @@ -48,7 +50,8 @@ tape("bin.value(function) sets the value accessor", (test) => {
bin([a, a, a], 0, 5),
bin([], 5, 10),
bin([b], 10, 15),
bin([c, c], 15, 20)
bin([], 15, 20),
bin([c, c], 20, 25)
]);
});

Expand Down Expand Up @@ -83,7 +86,8 @@ tape("bin.thresholds(number) sets the approximate number of bin thresholds", (te
test.deepEqual(h([0, 0, 0, 10, 30, 30]), [
bin([0, 0, 0], 0, 10),
bin([10], 10, 20),
bin([30, 30], 20, 30) // Note: inclusive upper bound for last bin.
bin([], 20, 30),
bin([30, 30], 30, 40)
]);
});

Expand All @@ -92,17 +96,17 @@ tape("bin.thresholds(array) sets the bin thresholds", (test) => {
test.deepEqual(h([0, 0, 0, 10, 30, 30]), [
bin([0, 0, 0], 0, 10),
bin([10], 10, 20),
bin([30, 30], 20, 30) // Note: inclusive upper bound for last bin.
bin([30, 30], 20, 30)
]);
});

tape("bin.thresholds(array) ignores thresholds outside the domain", (test) => {
const h = d3.bin().thresholds([0, 1, 2, 3]);
const h = d3.bin().thresholds([0, 1, 2, 3, 4]);
test.deepEqual(h([0, 1, 2, 3]), [
bin([0], 0, 1),
bin([1], 1, 2),
bin([2], 2, 3),
bin([3], 3, 3) // Note: inclusive upper bound for last bin.
bin([3], 3, 3)
]);
});

Expand All @@ -113,7 +117,7 @@ tape("bin.thresholds(function) sets the bin thresholds accessor", (test) => {
test.deepEqual(h(values), [
bin([0, 0, 0], 0, 10),
bin([10], 10, 20),
bin([30, 30], 20, 30) // Note: inclusive upper bound for last bin.
bin([30, 30], 20, 30)
]);
test.deepEqual(actual, [values, 0, 30]);
test.deepEqual(h.thresholds(() => 5)(values), [
Expand All @@ -122,7 +126,8 @@ tape("bin.thresholds(function) sets the bin thresholds accessor", (test) => {
bin([10], 10, 15),
bin([], 15, 20),
bin([], 20, 25),
bin([30, 30], 25, 30) // Note: inclusive upper bound for last bin.
bin([], 25, 30),
bin([30, 30], 30, 35)
]);
});

Expand All @@ -144,7 +149,7 @@ tape("bin()() returns bins whose rightmost bin is not too wide", (test) => {
bin([10], 10, 11),
bin([11], 11, 12),
bin([12], 12, 13),
bin([13, 13.2], 13, 14),
bin([13, 13.2], 13, 14)
]);
});

Expand Down

0 comments on commit 1a23ff0

Please sign in to comment.