observablehq / plot

A concise API for exploratory data visualization implementing a layered grammar of graphics
https://observablehq.com/plot/
ISC License
4.37k stars 176 forks source link

Facet wrapping #277

Open Fil opened 3 years ago

Fil commented 3 years ago

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 commented 3 years ago

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

mbostock commented 1 year ago

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.

tophtucker commented 1 year ago

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.

mbostock commented 1 year ago

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()
    ]
  };
})())
fgregg commented 1 year ago

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