d3-array
d3-array copied to clipboard
Missing bin due to floating-point error
const values = [0.9299999999999999, 1.07];
d3.bin().thresholds(500)(values).filter((d) => d.length);
this returns: [[1.07, x0: 1.07, x1: 1.0702]]; the first value has disappeared.
The problem occurs in the nice subroutine where the domain is erroneously niced to [0.93, 1.07]; the assumption is that the nice domain must subsume the original domain, but that’s not true with this input. That’s because:
Math.ceil(0.9299999999999999 * -5000) / -5000 // 0.93
Also:
d3.nice(0.9299999999999999, 1.07, 5000) // [0.93, 1.07]
The niced domain should be [0.9298, 1.07] instead. We probably need a threshold test instead of assuming that ceil and floor produce the desired result.
(That said, I’m curious where the 0.9299999999999999 is coming from? Because if we control that code, we should try to get it to generate 0.93 instead which would avoid this problem.)
This number is initially caused by a floating point error in this code I'm playing with:
const data = [0, 2, ...Array.from({ length: 18 }, () => 1)]; // Array(20)
const lo = d3.quantile(data, 0.05);
const hi = d3.quantile(data, 0.95);
const delta = (hi - lo) * 0.2;
const bins = d3.bin()
.thresholds(500)
.value((d) => clamp(d, lo - delta, hi + delta))(data);
return bins.filter((d) => d.length); // [Array(18), Array(1)] 🌶 there should be 20 total
function clamp(x, lo, hi) {
return x < lo ? lo : x > hi ? hi : x;
}
(I've "fixed" my code by using 1/7 instead of 0.2…)