diff --git a/src/options.js b/src/options.js
index ac9caca472..9fb056364f 100644
--- a/src/options.js
+++ b/src/options.js
@@ -54,7 +54,7 @@ function maybeTypedMap(data, f, type) {
return map(data, isNumberType(type) ? (d, i) => coerceNumber(f(d, i)) : f, type); // allow conversion from BigInt
}
-function maybeTypedArrayify(data, type) {
+export function maybeTypedArrayify(data, type) {
return type === undefined
? arrayify(data) // preserve undefined type
: isArrowVector(data)
diff --git a/src/transforms/normalize.js b/src/transforms/normalize.js
index f4d225cd11..846d2c6f43 100644
--- a/src/transforms/normalize.js
+++ b/src/transforms/normalize.js
@@ -1,6 +1,6 @@
import {extent, deviation, max, mean, median, min, sum} from "d3";
import {defined} from "../defined.js";
-import {percentile, taker} from "../options.js";
+import {maybeTypedArrayify, percentile, taker} from "../options.js";
import {mapX, mapY} from "./map.js";
export function normalizeX(basis, options) {
@@ -43,7 +43,8 @@ export function normalize(basis) {
function normalizeBasis(basis) {
return {
mapIndex(I, S, T) {
- const b = +basis(I, S);
+ S = maybeTypedArrayify(S, Float64Array);
+ const b = basis(I, S);
for (const i of I) {
T[i] = S[i] === null ? NaN : S[i] / b;
}
diff --git a/test/output/bigintNormalize.svg b/test/output/bigintNormalize.svg
new file mode 100644
index 0000000000..5c5b6435c8
--- /dev/null
+++ b/test/output/bigintNormalize.svg
@@ -0,0 +1,117 @@
+
\ No newline at end of file
diff --git a/test/output/bigintNormalizeMean.svg b/test/output/bigintNormalizeMean.svg
new file mode 100644
index 0000000000..846be5a5aa
--- /dev/null
+++ b/test/output/bigintNormalizeMean.svg
@@ -0,0 +1,86 @@
+
\ No newline at end of file
diff --git a/test/plots/bigint.ts b/test/plots/bigint.ts
index 0d526f75ca..201d54a850 100644
--- a/test/plots/bigint.ts
+++ b/test/plots/bigint.ts
@@ -25,3 +25,28 @@ export async function bigintOrdinal() {
export async function bigintStack() {
return Plot.barY(integers, {x: (d, i) => i % 5, y: "big1"}).plot();
}
+
+async function olympiansByWeight() {
+ const olympians = await d3.csv("data/athletes.csv", d3.autoType);
+ return d3
+ .bin()(olympians.map((d) => d.height))
+ .map((bin) => ({weightclass: bin.x0, count: BigInt(bin.length)}));
+}
+
+export async function bigintNormalize() {
+ return Plot.rectY(
+ d3.sort(await olympiansByWeight(), (d) => -d.count),
+ Plot.normalizeY({x: "weightclass", y: "count"})
+ ).plot();
+}
+
+export async function bigintNormalizeMean() {
+ return Plot.plot({
+ x: {interval: 0.05},
+ y: {label: "relative to mean"},
+ marks: [
+ Plot.barY(await olympiansByWeight(), Plot.normalizeY("mean", {x: "weightclass", y: "count"})),
+ Plot.ruleY([1], {stroke: "red"})
+ ]
+ });
+}
diff --git a/test/transforms/normalize-test.js b/test/transforms/normalize-test.js
index 43eb6c1ee0..9e11f520c8 100644
--- a/test/transforms/normalize-test.js
+++ b/test/transforms/normalize-test.js
@@ -5,6 +5,10 @@ it("Plot.normalize first", () => {
testNormalize([1, 2, 4, 5], "first", [1, 2, 4, 5]);
});
+it("Plot.normalize first BigInt", () => {
+ testNormalize([1n, 2n, 4n, 5n], "first", [1, 2, 4, 5]);
+});
+
it("Plot.normalize last", () => {
testNormalize([1, 2, 4, 5], "last", [0.2, 0.4, 0.8, 1]);
});
@@ -17,6 +21,10 @@ it("Plot.normalize median", () => {
testNormalize([1, 2, 6, 6], "median", [0.25, 0.5, 1.5, 1.5]);
});
+it("Plot.normalize median BigInt", () => {
+ testNormalize([1n, 2n, 6n, 6n], "median", [0.25, 0.5, 1.5, 1.5]);
+});
+
it("Plot.normalize min", () => {
testNormalize([10, 6, 2], "min", [5, 3, 1]);
});