plot icon indicating copy to clipboard operation
plot copied to clipboard

Facet wrapping

Open Fil opened this issue 4 years ago • 6 comments

We can create a grid facet with:

    x: d => (f(d) % 5),
    y: d => Math.floor(f(d) / 5)

where f returns an integer, maybe d => groups.indexOf(d.group).

Can we make it easier to compute the gridded facet, and to draw its mark)? (In this case we can't use fx/fy for the facet ticks, and also need a way to draw the facet's tick inside or below the frame at a fixed point.)

Examples:

Capture d’écran 2021-03-27 à 18 44 33

https://observablehq.com/d/61ca1967e419b882

Capture d’écran 2021-01-14 à 18 19 45 https://observablehq.com/@data-workflows/geofacet & https://github.com/observablehq/plot/pull/101

Fil avatar Mar 27 '21 17:03 Fil

another example here https://3iap.com/observable-plot-parking-plague-javascript-data-visualization-ukcPx9d7TqGaDLZUM2pkjQ/

Fil avatar May 20 '21 05:05 Fil

I retitled this to “facet wrap” since that seems to be the more common term for this. We made an attempt in #332 and in #892 but haven’t yet landed anything. The main issue seems to be how we represent the fx and fy scales/axes.

mbostock avatar Dec 02 '22 00:12 mbostock

I almost wonder if this should be an fz channel, since it uses both x and y? Hm… but I guess it still has a directional bias because it either goes left-right before wrapping or top-down before wrapping.

tophtucker avatar Apr 13 '23 22:04 tophtucker

Here is a new example. https://observablehq.com/@observablehq/plot-facet-wrap

untitled (64)

Plot.plot((() => {
  const n = 3; // number of facet columns
  const keys = Array.from(d3.union(industries.map((d) => d.industry)));
  const index = new Map(keys.map((key, i) => [key, i]));
  const fx = (key) => index.get(key) % n;
  const fy = (key) => Math.floor(index.get(key) / n);
  return {
    height: 300,
    axis: null,
    y: {insetTop: 10},
    fx: {padding: 0.03},
    marks: [
      Plot.areaY(industries, Plot.normalizeY("extent", {
        x: "date",
        y: "unemployed",
        fx: (d) => fx(d.industry),
        fy: (d) => fy(d.industry)
      })),
      Plot.text(keys, {fx, fy, frameAnchor: "top-left", dx: 6, dy: 6}),
      Plot.frame()
    ]
  };
})())

mbostock avatar Apr 24 '23 02:04 mbostock

i'd really appreciate if the wrap columns could adjust based on the width of the viewport

fgregg avatar Jul 10 '23 17:07 fgregg

I have managed to create small multiples with waffles, using the above technique fx: (d) => fx(d.industry).

Depending on the chart, there are additional challenges, such as padding for labels and in this case, keeping the circles round. https://observablehq.com/@spandl/waffle-chart-using-small-multiples

What's a bit odd is that the small multiples are rendered multiple times. (If you step through the code, each mark is passed twice, maybe even more on larger datasets)

I would love if this would be easier, but I also think there are many edge cases.

spandl avatar Jan 08 '25 23:01 spandl