Faceting: fx and fy channels for small multiples
Faceting partitions data by ordinal or categorical value and repeats a plot for each partition (each facet), producing small multiples for comparison. Faceting is enabled by declaring the fx channel for horizontal facets, the fy channel for vertical facets, or both for two-dimensional faceting.
Faceting requires ordinal or categorical data
Faceting requires ordinal or categorical data because there are a discrete number of facets. The associated fx and fy scales are band scales. Quantitative or temporal data can be made ordinal by binning (such as with Math.floor) or by using the interval scale option on the fx or fy scale.
Mark facet option values
The facet mark option accepts the following values: auto (default) - automatically determine if this mark should be faceted; include (or true) - draw the subset of the mark's data in the current facet; exclude - draw the subset of the mark's data not in the current facet; super - draw this mark in a single frame that covers all facets; null (or false) - repeat this mark's data across all facets (no faceting).
facetAnchor option controls mark placement with respect to facets
The facetAnchor option controls the placement of the mark with respect to the facets. Values are: null (default for most marks) - display on non-empty facets; top, right, bottom, or left - display on the given side; top-empty, right-empty, bottom-empty, or left-empty - display adjacent to empty facet or side; empty - display on empty facets. For axis marks, the default depends on the axis orientation and associated scale.
Mixing faceted and non-faceted marks
You can mix faceted and non-faceted marks within the same plot. Non-faceted marks will be repeated across all facets. This is useful for decoration marks such as frame and also for context, allowing the entire population to be repeated in each facet as background marks.
Plot facet option properties
The facet plot option provides additional control over facet position scales and axes with the following properties: marginTop - the top margin; marginRight - the right margin; marginBottom - the bottom margin; marginLeft - the left margin; margin - shorthand for the four margins; grid - if true, draw grid lines for each facet; label - if null, disable default facet axis labels.
Plot-level facet option for data
The facet plot option is an alternative to fx and fy mark options. It accepts data, x, and y properties: data - the data to be faceted; x - the horizontal position, bound to the fx scale; y - the vertical position, bound to the fy scale. When top-level faceting is used, any mark that uses the specified facet data will be faceted by default, whereas marks using different data will be repeated across all facets.
Facet scales: fx and fy band scales
When faceting, two additional band scales may be configured: fx - the horizontal position, a band scale; fy - the vertical position, a band scale. The space between facets can be adjusted using the padding, round, and align scale options.
fx and fy channels are computed before mark transforms
The fx and fy channels are computed prior to the mark's transform, if any. This means facet channels are not transformed.
Top-level faceting: parallel data requirement for include and exclude
When top-level faceting is used with include or exclude facet modes, the mark data must be parallel to the top-level facet data: the data must have the same length and order. If the data are not parallel, then the wrong data may be shown in each facet. The default auto setting requires strict equality for safety, and using the facet data as mark data is recommended when using exclude facet mode.
Example: vertical faceting with sort option
Plot.plot({
height: 800,
marginRight: 90,
marginLeft: 110,
grid: true,
x: {nice: true},
y: {inset: 5},
color: {type: "categorical"},
marks: [
Plot.frame(),
Plot.dot(barley, {
x: "yield",
y: "variety",
fy: "site",
stroke: "year",
sort: {y: "-x", fy: "-x", reduce: "median"}
})
]
})
This example creates a Trellis display using the dot mark's fy channel for vertical facets, showing barley yields across sites for different years, with y and fy domains sorted by descending median yield.
Example: two-dimensional faceting
Plot.plot({
grid: true,
marginRight: 60,
facet: {label: null},
marks: [
Plot.frame(),
Plot.dot(penguins, {
x: "culmen_length_mm",
y: "culmen_depth_mm",
fx: "sex",
fy: "species"
})
]
})
This example creates a two-dimensional faceted scatterplot where the horizontal facet shows sex (with the rightmost column representing penguins with null sex) and the vertical facet shows species.
Example: facet wrapping with computed fx and fy
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()
]
};
})())
This example wraps facets by computing row and column numbers as fy and fx, showing small multiples in a three-column layout with text labels and frame marks.
Example: faceted annotations with single-element array
Plot.text([`While Chinstrap and Gentoo penguins were each observed on only one island, Adelie penguins were observed on all three islands.`], {
fy: ["Adelie"],
frameAnchor: "top-right",
lineWidth: 18,
dx: -6,
dy: 6
})
This applies a text annotation to a single facet by setting the fy channel to a single-element array parallel to the data.
Example: plot-level faceting with Anscombe's quartet
Plot.plot({
grid: true,
aspectRatio: 0.5,
facet: {data: anscombe, x: "series"},
marks: [
Plot.frame(),
Plot.line(anscombe, {x: "x", y: "y"}),
Plot.dot(anscombe, {x: "x", y: "y"})
]
})
This example visualizes Anscombe's quartet as a scatterplot with horizontal facets using top-level faceting. Multiple marks share the same facet data and are faceted automatically.
Example: interval scale option for binned faceting
Plot.plot({
fy: {
grid: true,
tickFormat: ".1f",
interval: 0.1, // 10cm
reverse: true
},
marks: [
Plot.boxX(olympians.filter((d) => d.height), {x: "weight", fy: "height"})
]
})
This example produces a box plot of athlete weights faceted by height binned at a 10cm (0.1 meter) interval using the interval scale option on the fy scale.
Example: non-faceted marks for context
Plot.plot({
grid: true,
marginRight: 60,
facet: {label: null},
marks: [
Plot.frame(),
Plot.dot(penguins, {
x: "culmen_length_mm",
y: "culmen_depth_mm",
fill: "#aaa",
r: 1
}),
Plot.dot(penguins, {
x: "culmen_length_mm",
y: "culmen_depth_mm",
fx: "sex",
fy: "species"
})
]
})
This example shows how to mix faceted and non-faceted marks, with the entire population of penguins repeated in each facet as small gray dots (context), and a faceted version showing only the subset in each facet.
facetAnchor option values
The mark.facetAnchor option controls which facets show an axis. Supported values: top (show only on the top facets), right (show only on the right facets), bottom (show only on the bottom facets), left (show only on the left facets), top-empty (show on any facet with space above; a superset of top), right-empty (show on any facet with space to the right; a superset of right), bottom-empty (show on any facet with space below; a superset of bottom), left-empty (show on any facet with space to the left; a superset of left), null (show on every facet).
Axis facetAnchor defaults
The axis mark's facetAnchor option defaults to top-empty if anchor is top, right-empty if anchor is right, bottom-empty if anchor is bottom, and left-empty if anchor is left. This ensures the proper positioning of the axes with respect to empty facets.
Axis in faceted plots behavior
When faceting, the x- and y-axes are typically repeated across facets. A bottom-anchored x-axis is by default drawn on any facet with empty space below it; conversely, a top-anchored x-axis is drawn on any facet with empty space above it. Similarly, a left-anchored y-axis is drawn on facets with empty space to the left, and a right-anchored y-axis is drawn on facets with empty space to the right.
density mark threshold sharing across facets and series
To facilitate comparison across facets (fx or fy) and series (z, stroke, or fill), the thresholds are determined by the series with the highest density. All other facets and series use the same thresholds for consistent visual comparison.
geo mark supports faceting
The geo mark supports faceting.
Faceted histogram with grid and rule example
The following code creates a faceted histogram showing weight distributions separately by sex with grid lines and a rule at y=0: Plot.plot({grid: true, marks: [Plot.rectY(olympians, Plot.binX({y: "count"}, {x: "weight", fill: "sex", fy: "sex"})), Plot.ruleY([0])]})