diff --git a/src/marks/auto.js b/src/marks/auto.js index b53c632ef0..4724a98f68 100644 --- a/src/marks/auto.js +++ b/src/marks/auto.js @@ -1,6 +1,6 @@ import {ascending, InternSet} from "d3"; import {marks} from "../mark.js"; -import {isColor, isObject, isOptions, isOrdinal, labelof, valueof} from "../options.js"; +import {isColor, isNumeric, isObject, isOptions, isOrdinal, labelof, valueof} from "../options.js"; import {bin, binX, binY} from "../transforms/bin.js"; import {group, groupX, groupY} from "../transforms/group.js"; import {areaX, areaY} from "./area.js"; @@ -113,27 +113,32 @@ export function autoSpec(data, options) { colorMode = "stroke"; break; case "bar": - markImpl = yZero - ? isOrdinalReduced(xReduce, X) - ? barY - : rectY - : xZero - ? isOrdinalReduced(yReduce, Y) - ? barX - : rectX - : isOrdinalReduced(xReduce, X) && isOrdinalReduced(yReduce, Y) - ? cell - : isOrdinalReduced(xReduce, X) - ? barY - : isOrdinalReduced(yReduce, Y) - ? barX - : xReduce != null - ? rectX - : yReduce != null - ? rectY - : colorReduce != null - ? rect - : cell; + markImpl = + xReduce != null // bin or group on y + ? isOrdinal(Y) + ? isSelectReducer(xReduce) && X && isOrdinal(X) + ? cell + : barX + : rectX + : yReduce != null // bin or group on x + ? isOrdinal(X) + ? isSelectReducer(yReduce) && Y && isOrdinal(Y) + ? cell + : barY + : rectY + : colorReduce != null || sizeReduce != null // bin or group on both x and y + ? X && isOrdinal(X) && Y && isOrdinal(Y) + ? cell + : X && isOrdinal(X) + ? barY + : Y && isOrdinal(Y) + ? barX + : rect + : X && isNumeric(X) && !(Y && isNumeric(Y)) + ? barX // if y is temporal, treat as ordinal + : Y && isNumeric(Y) && !(X && isNumeric(X)) + ? barY // if x is temporal, treat as ordinal + : cell; colorMode = "fill"; break; default: @@ -300,12 +305,6 @@ function isSelectReducer(reduce) { return /^(?:first|last|mode)$/i.test(reduce); } -// We can’t infer the type of a custom reducer without invoking it, so -// assume most reducers produce quantitative values. -function isOrdinalReduced(reduce, value) { - return (reduce != null && !isSelectReducer(reduce)) || !value ? false : isOrdinal(value); -} - // https://github.com/observablehq/plot/blob/818562649280e155136f730fc496e0b3d15ae464/src/transforms/group.js#L236 function isReducer(reduce) { if (reduce == null) return false; diff --git a/test/output/autoBarTimeSeries.svg b/test/output/autoBarTimeSeries.svg new file mode 100644 index 0000000000..6a7dbd2ed1 --- /dev/null +++ b/test/output/autoBarTimeSeries.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + + + ↑ value + + + + + + + + + + + 2023-04-01 + 2023-04-05 + 2023-04-10 + 2023-04-15 + 2023-04-20 + 2023-04-25 + + + date + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/autoBarTimeSeriesReduce.svg b/test/output/autoBarTimeSeriesReduce.svg new file mode 100644 index 0000000000..058c8bf0cc --- /dev/null +++ b/test/output/autoBarTimeSeriesReduce.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + 0 + 2 + 4 + 6 + 8 + 10 + 12 + 14 + 16 + 18 + + + ↑ value + + + + + + + + + + + 26Mar + 2Apr + 9 + 16 + 23 + 30 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/autoplot.ts b/test/plots/autoplot.ts index 0b876f3326..619a8639c6 100644 --- a/test/plots/autoplot.ts +++ b/test/plots/autoplot.ts @@ -103,6 +103,24 @@ export async function autoBar() { return Plot.auto(alphabet, {x: "frequency", y: "letter", mark: "bar"}).plot(); } +const timeSeries = [ + {date: new Date("2023-04-01"), type: "triangle", value: 5}, + {date: new Date("2023-04-05"), type: "circle", value: 7}, + {date: new Date("2023-04-10"), type: "circle", value: 8}, + {date: new Date("2023-04-15"), type: "circle", value: 3}, + {date: new Date("2023-04-15"), type: "triangle", value: 7}, + {date: new Date("2023-04-20"), type: "triangle", value: 4}, + {date: new Date("2023-04-25"), type: "square", value: 5} +]; + +export async function autoBarTimeSeries() { + return Plot.auto(timeSeries, {x: "date", y: "value", color: "type", mark: "bar"}).plot({x: {type: "band"}}); // TODO suppress warning? +} + +export async function autoBarTimeSeriesReduce() { + return Plot.auto(timeSeries, {x: "date", y: {value: "value", reduce: "sum"}, color: "type", mark: "bar"}).plot(); +} + export async function autoConnectedScatterplot() { const driving = await d3.csv("data/driving.csv", d3.autoType); return Plot.auto(driving, {x: "miles", y: "gas", mark: "line"}).plot();