Skip to content

Commit 92fee30

Browse files
committed
fix facet channel sort with transform
1 parent 46a3cdd commit 92fee30

File tree

5 files changed

+290
-3
lines changed

5 files changed

+290
-3
lines changed

src/channel.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,14 @@ export function inferChannelScale(name, channel) {
6969
// Note: mutates channel.domain! This is set to a function so that it is lazily
7070
// computed; i.e., if the scale’s domain is set explicitly, that takes priority
7171
// over the sort option, and we don’t need to do additional work.
72-
export function channelDomain(channels, facetChannels, data, options) {
72+
export function channelDomain(data, facets, channels, facetChannels, options) {
7373
const {reverse: defaultReverse, reduce: defaultReduce = true, limit: defaultLimit} = options;
7474
for (const x in options) {
7575
if (!registry.has(x)) continue; // ignore unknown scale keys (including generic options)
7676
let {value: y, reverse = defaultReverse, reduce = defaultReduce, limit = defaultLimit} = maybeValue(options[x]);
7777
if (reverse === undefined) reverse = y === "width" || y === "height"; // default to descending for lengths
7878
if (reduce == null || reduce === false) continue; // disabled reducer
79-
const X = findScaleChannel(channels, x) || (facetChannels && findScaleChannel(facetChannels, x));
79+
const X = x === "fx" || x === "fy" ? reindexFacetChannel(facets, facetChannels[x]) : findScaleChannel(channels, x);
8080
if (!X) throw new Error(`missing channel for scale: ${x}`);
8181
const XV = X.value;
8282
const [lo = 0, hi = Infinity] = isIterable(limit) ? limit : limit < 0 ? [limit] : [0, limit];
@@ -118,6 +118,21 @@ function findScaleChannel(channels, scale) {
118118
}
119119
}
120120

121+
// Facet channels are not affected by transforms; so, to compute the domain of a
122+
// facet scale, we must first re-index the facet channel according to the
123+
// transformed mark index. Note: mutates channel, but that should be safe here?
124+
function reindexFacetChannel(facets, channel) {
125+
const originalFacets = facets.original;
126+
if (originalFacets === facets) return channel; // not transformed
127+
const V1 = channel.value;
128+
const V2 = (channel.value = []); // mutates channel!
129+
for (let i = 0; i < originalFacets.length; ++i) {
130+
const vi = V1[originalFacets[i][0]];
131+
for (const j of facets[i]) V2[j] = vi;
132+
}
133+
return channel;
134+
}
135+
121136
function difference(channels, k1, k2) {
122137
const X1 = values(channels, k1);
123138
const X2 = values(channels, k2);

src/mark.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,11 @@ export class Mark {
7878
initialize(facets, facetChannels) {
7979
let data = arrayify(this.data);
8080
if (facets === undefined && data != null) facets = [range(data)];
81+
const originalFacets = facets;
8182
if (this.transform != null) ({facets, data} = this.transform(data, facets)), (data = arrayify(data));
83+
if (facets !== undefined) facets.original = originalFacets; // needed up read facetChannels
8284
const channels = Channels(this.channels, data);
83-
if (this.sort != null) channelDomain(channels, facetChannels, data, this.sort); // mutates facetChannels!
85+
if (this.sort != null) channelDomain(data, facets, channels, facetChannels, this.sort); // mutates facetChannels!
8486
return {data, facets, channels};
8587
}
8688
filter(index, channels, values) {

test/output/athletesSort.svg

Lines changed: 261 additions & 0 deletions
Loading

test/plots/athletes-sort.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Plot from "@observablehq/plot";
2+
import * as d3 from "d3";
3+
4+
export async function athletesSort() {
5+
const athletes = await d3.csv("data/athletes.csv", d3.autoType);
6+
const female = (d) => d.sex === "female";
7+
return Plot.barX(athletes, Plot.groupZ({x: "mean"}, {x: female, fy: "sport", sort: {fy: "x"}})).plot();
8+
}

test/plots/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ export * from "./aapl-close.js";
281281
export * from "./aapl-fancy-axis.js";
282282
export * from "./aspectRatio.js";
283283
export * from "./athletes-sample.js";
284+
export * from "./athletes-sort.js";
284285
export * from "./autoplot.js";
285286
export * from "./axis-labels.js";
286287
export * from "./bigint.js";

0 commit comments

Comments
 (0)