Skip to content

Commit

Permalink
better degenerate thresholds (#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Mar 20, 2023
1 parent 7952835 commit cdd37e4
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/threshold/freedmanDiaconis.js
Expand Up @@ -2,5 +2,6 @@ import count from "../count.js";
import quantile from "../quantile.js";

export default function thresholdFreedmanDiaconis(values, min, max) {
return Math.ceil((max - min) / (2 * (quantile(values, 0.75) - quantile(values, 0.25)) * Math.pow(count(values), -1 / 3)));
const c = count(values), d = quantile(values, 0.75) - quantile(values, 0.25);
return c && d ? Math.ceil((max - min) / (2 * d * Math.pow(c, -1 / 3))) : 1;
}
3 changes: 2 additions & 1 deletion src/threshold/scott.js
Expand Up @@ -2,5 +2,6 @@ import count from "../count.js";
import deviation from "../deviation.js";

export default function thresholdScott(values, min, max) {
return Math.ceil((max - min) * Math.cbrt(count(values)) / (3.49 * deviation(values)));
const c = count(values), d = deviation(values);
return c && d ? Math.ceil((max - min) * Math.cbrt(c) / (3.49 * d)) : 1;
}
2 changes: 1 addition & 1 deletion src/threshold/sturges.js
@@ -1,5 +1,5 @@
import count from "../count.js";

export default function thresholdSturges(values) {
return Math.ceil(Math.log(count(values)) / Math.LN2) + 1;
return Math.max(1, Math.ceil(Math.log(count(values)) / Math.LN2) + 1);
}
1 change: 1 addition & 0 deletions test/deviation-test.js
Expand Up @@ -2,6 +2,7 @@ import assert from "assert";
import {deviation} from "../src/index.js";

it("deviation(array) returns the standard deviation of the specified numbers", () => {
assert.strictEqual(deviation([1, 1, 1, 1, 1]), 0);
assert.strictEqual(deviation([5, 1, 2, 3, 4]), Math.sqrt(2.5));
assert.strictEqual(deviation([20, 3]), Math.sqrt(144.5));
assert.strictEqual(deviation([3, 20]), Math.sqrt(144.5));
Expand Down
12 changes: 12 additions & 0 deletions test/threshold/freedmanDiaconic-test.js
Expand Up @@ -4,3 +4,15 @@ import {thresholdFreedmanDiaconis} from "../../src/index.js";
it("thresholdFreedmanDiaconis(values, min, max) returns the expected result", () => {
assert.strictEqual(thresholdFreedmanDiaconis([4, 3, 2, 1, NaN], 1, 4), 2);
});

it("thresholdFreedmanDiaconis(values, min, max) handles values with zero deviation", () => {
assert.strictEqual(thresholdFreedmanDiaconis([1, 1, 1, 1], 1, 4), 1);
});

it("thresholdFreedmanDiaconis(values, min, max) handles single-value arrays", () => {
assert.strictEqual(thresholdFreedmanDiaconis([1], 1, 4), 1);
});

it("thresholdFreedmanDiaconis(values, min, max) handles empty arrays", () => {
assert.strictEqual(thresholdFreedmanDiaconis([], 1, 4), 1);
});
12 changes: 12 additions & 0 deletions test/threshold/scott-test.js
Expand Up @@ -4,3 +4,15 @@ import {thresholdScott} from "../../src/index.js";
it("thresholdScott(values, min, max) returns the expected result", () => {
assert.strictEqual(thresholdScott([4, 3, 2, 1, NaN], 1, 4), 2);
});

it("thresholdScott(values, min, max) handles values with zero deviation", () => {
assert.strictEqual(thresholdScott([1, 1, 1, 1], 1, 4), 1);
});

it("thresholdScott(values, min, max) handles single-value arrays", () => {
assert.strictEqual(thresholdScott([1], 1, 4), 1);
});

it("thresholdScott(values, min, max) handles empty arrays", () => {
assert.strictEqual(thresholdScott([], 1, 4), 1);
});
5 changes: 5 additions & 0 deletions test/threshold/sturges-test.js
Expand Up @@ -3,4 +3,9 @@ import {thresholdSturges} from "../../src/index.js";

it("thresholdSturges(values, min, max) returns the expected result", () => {
assert.strictEqual(thresholdSturges([4, 3, 2, 1, NaN], 1, 4), 3);
assert.strictEqual(thresholdSturges([1], 1, 4), 1);
});

it("thresholdSturges(values, min, max) handles empty arrays", () => {
assert.strictEqual(thresholdSturges([], 1, 4), 1);
});

0 comments on commit cdd37e4

Please sign in to comment.