Skip to content

Commit d8378f0

Browse files
authored
fix bars with maybe-ordinal reducers (#1276)
1 parent e051e5d commit d8378f0

File tree

3 files changed

+82
-29
lines changed

3 files changed

+82
-29
lines changed

src/marks/auto.js

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function auto(data, {x, y, color, size, fx, fy, mark} = {}) {
5151
// TODO Limit and sort for bar charts (e.g. alphabet)?
5252
// TODO Look at Plot warnings and see how many we can prevent
5353
// TODO Default to something other than turbo for continuous? Like:
54-
// scheme: (colorValue && isContinuous(color)) || colorReduce ? "ylgnbu" : undefined
54+
// scheme: (colorValue && !isOrdinal(color)) || colorReduce ? "ylgnbu" : undefined
5555

5656
// To apply heuristics based on the data types (values), realize the columns.
5757
// We could maybe look at the data.schema here, but Plot’s behavior depends on
@@ -100,9 +100,9 @@ export function auto(data, {x, y, color, size, fx, fy, mark} = {}) {
100100
: xZero || yZero || colorReduce != null // histogram or heatmap
101101
? "bar"
102102
: x && y
103-
? isContinuous(x) && isContinuous(y) && (xReduce != null || yReduce != null || isMonotonic(x) || isMonotonic(y))
104-
? "line"
105-
: "dot"
103+
? isOrdinal(x) || isOrdinal(y) || (xReduce == null && yReduce == null && !isMonotonic(x) && !isMonotonic(y))
104+
? "dot"
105+
: "line"
106106
: x || y
107107
? "rule"
108108
: null;
@@ -132,28 +132,21 @@ export function auto(data, {x, y, color, size, fx, fy, mark} = {}) {
132132
colorMode = "stroke";
133133
break;
134134
case "bar":
135-
mark =
136-
yReduce != null
137-
? isOrdinal(x)
138-
? barY
139-
: rectY
140-
: xReduce != null
141-
? isOrdinal(y)
142-
? barX
143-
: rectX
144-
: colorReduce != null
145-
? x && y && isOrdinal(x) && isOrdinal(y)
146-
? cell
147-
: x && isOrdinal(x)
148-
? barY
149-
: y && isOrdinal(y)
150-
? barX
151-
: rect
152-
: x && y && isOrdinal(x) && isOrdinal(y)
153-
? cell
154-
: y && isOrdinal(y)
135+
mark = yZero
136+
? isOrdinalReduced(xReduce, x)
137+
? barY
138+
: rectY
139+
: xZero
140+
? isOrdinalReduced(yReduce, y)
155141
? barX
156-
: barY;
142+
: rectX
143+
: isOrdinalReduced(xReduce, x) && isOrdinalReduced(yReduce, y)
144+
? cell
145+
: isOrdinalReduced(xReduce, x)
146+
? barY
147+
: isOrdinalReduced(yReduce, y)
148+
? barX
149+
: rect;
157150
colorMode = "fill";
158151
break;
159152
default:
@@ -198,10 +191,6 @@ export function auto(data, {x, y, color, size, fx, fy, mark} = {}) {
198191
return colorMode === "stroke" ? marks(frames, rules, mark) : marks(frames, mark, rules);
199192
}
200193

201-
function isContinuous(values) {
202-
return !isOrdinal(values);
203-
}
204-
205194
// TODO What about sorted within series?
206195
function isMonotonic(values) {
207196
let previous;
@@ -225,10 +214,22 @@ function makeOptions(value) {
225214
return isReducer(value) ? {reduce: value} : {value};
226215
}
227216

217+
// The distinct, count, sum, and proportion reducers are additive (stackable).
228218
function isZeroReducer(reduce) {
229219
return /^(?:distinct|count|sum|proportion)$/i.test(reduce);
230220
}
231221

222+
// The first, last, and mode reducers preserve the type of the aggregated values.
223+
function isSelectReducer(reduce) {
224+
return /^(?:first|last|mode)$/i.test(reduce);
225+
}
226+
227+
// We can’t infer the type of a custom reducer without invoking it, so
228+
// assume most reducers produce quantitative values.
229+
function isOrdinalReduced(reduce, value) {
230+
return (reduce != null && !isSelectReducer(reduce)) || !value ? false : isOrdinal(value);
231+
}
232+
232233
// https://github.com/observablehq/plot/blob/818562649280e155136f730fc496e0b3d15ae464/src/transforms/group.js#L236
233234
function isReducer(reduce) {
234235
if (typeof reduce?.reduce === "function" && isObject(reduce)) return true; // N.B. array.reduce

test/output/autoBarMode.svg

Lines changed: 47 additions & 0 deletions
Loading

test/plots/autoplot.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ export async function autoBarMeanZero() {
197197
return Plot.auto(weather, {x: "date", y: {value: "temp_max", reduce: "mean", zero: true}}).plot();
198198
}
199199

200+
export async function autoBarMode() {
201+
const penguins = await d3.csv("data/penguins.csv", d3.autoType);
202+
return Plot.auto(penguins, {x: "island", y: {value: "species", reduce: "mode"}, mark: "bar"}).plot();
203+
}
204+
200205
export async function autoLineMean() {
201206
const weather = await d3.csv("data/seattle-weather.csv", d3.autoType);
202207
return Plot.auto(weather, {x: "date", y: {value: "temp_max", reduce: "mean"}}).plot();

0 commit comments

Comments
 (0)