Skip to content

Commit a033aa2

Browse files
authored
preserve dates for mean and median (#1113)
* preserve dates for mean and median * more reducer tests
1 parent 785d03e commit a033aa2

File tree

2 files changed

+78
-3
lines changed

2 files changed

+78
-3
lines changed

src/transforms/group.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import {
2828
labelof,
2929
range,
3030
second,
31-
percentile
31+
percentile,
32+
isTemporal
3233
} from "../options.js";
3334
import {basic} from "./basic.js";
3435

@@ -260,9 +261,9 @@ export function maybeReduce(reduce, value) {
260261
case "max-index":
261262
return reduceAccessor(maxIndex);
262263
case "mean":
263-
return reduceAccessor(mean);
264+
return reduceMaybeTemporalAccessor(mean);
264265
case "median":
265-
return reduceAccessor(median);
266+
return reduceMaybeTemporalAccessor(median);
266267
case "variance":
267268
return reduceAccessor(variance);
268269
case "mode":
@@ -319,6 +320,15 @@ function reduceAccessor(f) {
319320
};
320321
}
321322

323+
function reduceMaybeTemporalAccessor(f) {
324+
return {
325+
reduce(I, X) {
326+
const x = f(I, (i) => X[i]);
327+
return isTemporal(X) ? new Date(x) : x;
328+
}
329+
};
330+
}
331+
322332
export const reduceIdentity = {
323333
reduce(I, X) {
324334
return take(X, I);

test/transforms/group-test.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as Plot from "@observablehq/plot";
22
import assert from "assert";
3+
import * as d3 from "d3";
34

45
it("Plot.group does not return unspecified options", () => {
56
const A = Plot.group({});
@@ -34,3 +35,67 @@ it("Plot.group does return specified options", () => {
3435
assert.strictEqual("fill" in C, false);
3536
assert.strictEqual("stroke" in C, false);
3637
});
38+
39+
it("Plot.group’s min reducer does not coerce numbers and uses natural order", () => {
40+
const group = Plot.groupZ({y: "min"}, {y: "y"});
41+
const data = [{y: "10"}, {y: "4"}, {y: "2"}];
42+
const facets = [d3.range(data.length)];
43+
const {data: groupData} = group.transform(data, facets);
44+
assert.deepStrictEqual(Plot.valueof(groupData, group.y), ["10"]);
45+
});
46+
47+
it("Plot.group’s max reducer does not coerce numbers and uses natural order", () => {
48+
const group = Plot.groupZ({y: "max"}, {y: "y"});
49+
const data = [{y: "10"}, {y: "4"}, {y: "2"}];
50+
const facets = [d3.range(data.length)];
51+
const {data: groupData} = group.transform(data, facets);
52+
assert.deepStrictEqual(Plot.valueof(groupData, group.y), ["4"]);
53+
});
54+
55+
it("Plot.group’s percentile reducer coerces numbers", () => {
56+
const group = Plot.groupZ({y: "p00"}, {y: "y"});
57+
const data = [{y: "1"}, {y: "4"}, {y: "2"}];
58+
const facets = [d3.range(data.length)];
59+
const {data: groupData} = group.transform(data, facets);
60+
assert.deepStrictEqual(Plot.valueof(groupData, group.y), [1]);
61+
});
62+
63+
it("Plot.group’s median reducer coerces numbers", () => {
64+
const group = Plot.groupZ({y: "median"}, {y: "y"});
65+
const data = [{y: "1"}, {y: "4"}, {y: "2"}];
66+
const facets = [d3.range(data.length)];
67+
const {data: groupData} = group.transform(data, facets);
68+
assert.deepStrictEqual(Plot.valueof(groupData, group.y), [2]);
69+
});
70+
71+
it("Plot.group’s median reducer ignores NaN, undefined, and null", () => {
72+
const group = Plot.groupZ({y: "median"}, {y: "y"});
73+
const data = [{y: "1"}, {y: "4"}, {y: null}, {y: null}, {y: NaN}, {}, {y: "2"}];
74+
const facets = [d3.range(data.length)];
75+
const {data: groupData} = group.transform(data, facets);
76+
assert.deepStrictEqual(Plot.valueof(groupData, group.y), [2]);
77+
});
78+
79+
it("Plot.group’s median reducer preserves dates", () => {
80+
const group = Plot.groupZ({y: "median"}, {y: "y"});
81+
const data = [{y: new Date("2021-01-01")}, {y: new Date("2024-01-01")}, {y: new Date("2022-01-01")}];
82+
const facets = [d3.range(data.length)];
83+
const {data: groupData} = group.transform(data, facets);
84+
assert.deepStrictEqual(Plot.valueof(groupData, group.y), [new Date("2022-01-01")]);
85+
});
86+
87+
it("Plot.group’s mean reducer preserves dates", () => {
88+
const group = Plot.groupZ({y: "mean"}, {y: "y"});
89+
const data = [{y: new Date("2021-01-01")}, {y: new Date("2024-01-01")}, {y: new Date("2022-01-01")}];
90+
const facets = [d3.range(data.length)];
91+
const {data: groupData} = group.transform(data, facets);
92+
assert.deepStrictEqual(Plot.valueof(groupData, group.y), [new Date("2022-05-02T16:00:00Z")]);
93+
});
94+
95+
it("Plot.group’s min reducer preserves dates", () => {
96+
const group = Plot.groupZ({y: "min"}, {y: "y"});
97+
const data = [{y: new Date("2021-01-01")}, {y: new Date("2024-01-01")}, {y: new Date("2022-01-01")}];
98+
const facets = [d3.range(data.length)];
99+
const {data: groupData} = group.transform(data, facets);
100+
assert.deepStrictEqual(Plot.valueof(groupData, group.y), [new Date("2021-01-01")]);
101+
});

0 commit comments

Comments
 (0)