Facet grids: facet_grid
Mix.install([
{:giggly, path: Path.join(__DIR__, "..")},
{:kino, "~> 0.19"}
])
alias Giggly.Plot
mpg = Giggly.Datasets.mpg()
mtcars = Giggly.Datasets.mtcars()
render = fn plot ->
plot
|> Giggly.to_svg()
|> IO.iodata_to_binary()
|> Kino.Image.new(:svg)
end
About this notebook
This notebook follows the examples on ggplot2's reference page for
facet_grid(), in the order the page
gives them. Each example shows ggplot2's code first and then Giggly's. An example Giggly cannot
draw yet keeps its ggplot2 code and names what it is waiting for, so the page is also a list of
what remains.
facet_grid() lays a plot's panels out as a matrix: a row of panels for each value of one set of
columns and a column of panels for each value of another. It suits two discrete columns whose
every combination occurs in the data; for one column with many values, ggplot2 suggests
facet_wrap(). Its scales can be shared by every panel or free to vary along the rows or the
columns, panels can take space in proportion to their scales, and margins add panels that hold
the data for every value of a column. Giggly has Giggly.Plot.facet_grid/2, which takes the
rows' columns as rows:, the columns' as cols:, and scales:, shared or free along either. It
takes no space, margins, axes, axis.labels, labeller, switch, as.table, drop or
shrink options. A plot is described with Giggly.Plot and drawn with Giggly.to_svg/2, which
returns an SVG document. The setup cell aliases Giggly.Plot, so the cells write Plot.new/2,
and defines render, which draws a plot and shows it below the cell with Kino.Image. The data
is Giggly.Datasets.mpg/0, ggplot2's mpg: fuel economy for 234 cars of 38 popular models from
1999 to 2008; and Giggly.Datasets.mtcars/0, R's mtcars: 32 cars from the 1974 Motor Trend
US magazine. The setup cell binds them to mpg and mtcars, R's names for them.
Rows, columns, or both
p <- ggplot(mpg, aes(displ, cty)) + geom_point()
# Use vars() to supply variables from the dataset:
p + facet_grid(rows = vars(drv))
p + facet_grid(cols = vars(cyl))
p + facet_grid(vars(drv), vars(cyl))
ggplot2 names the columns with vars(), and the last call passes the rows' and the columns' by
position, rows first; Giggly names each column as a mapping does, by an atom or a string, given to
rows: or cols:.
plot =
mpg
|> Plot.new(%{x: :displ, y: :cty})
|> Plot.geom_point()
plot
|> Plot.facet_grid(rows: :drv)
|> render.()
plot
|> Plot.facet_grid(cols: :cyl)
|> render.()
plot
|> Plot.facet_grid(rows: :drv, cols: :cyl)
|> render.()
The order of the panels
# To change plot order of facet grid,
# change the order of variable levels with factor()
This is advice rather than an example: the panels follow the order of a column's levels, which R
sets with factor(). Giggly.Plot.facet_grid/2's rows and columns of panels follow a column's
levels too, which Giggly.Factor.new/2 sets in the table.
Data repeated across panels
df is a single row, the mean displ and cty of all the cars, drawn as a red point in every
panel.
# If you combine a facetted dataset with a dataset that lacks those
# faceting variables, the data will be repeated across the missing
# combinations:
df <- data.frame(displ = mean(mpg$displ), cty = mean(mpg$cty))
p +
facet_grid(cols = vars(cyl)) +
geom_point(data = df, colour = "red", size = 2)
The means are base R's mean(), so the cell writes out the two values R gives. A layer given a
table of its own by data: draws that table, and one lacking the facet's columns is drawn in every
panel.
df = %{"displ" => [3.471794871794872], "cty" => [16.858974358974358]}
plot
|> Plot.facet_grid(cols: :cyl)
|> Plot.geom_point(data: df, colour: "red", size: 2)
|> render.()
Axes on every panel
year ~ drv is facet_grid()'s formula form: the rows' columns left of the ~, the columns'
right of it, and . for none.
# When scales are constant, duplicated axes can be shown with
# or without labels
ggplot(mpg, aes(cty, hwy)) +
geom_point() +
facet_grid(year ~ drv, axes = "all", axis.labels = "all_x")
Not in Giggly yet: axes and axis.labels, which here draw both axes on every panel but label
only the x axes.
Free scales
# Free scales -------------------------------------------------------
# You can also choose whether the scales should be constant
# across all panels (the default), or whether they should be allowed
# to vary
mt <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
geom_point()
mt + facet_grid(vars(cyl), scales = "free")
{:factor, [:cyl]} is R's factor(cyl), which makes the colour discrete. With scales: :free,
each column of panels has an x scale of its own and each row a y scale.
mt =
mtcars
|> Plot.new(%{x: :mpg, y: :wt, colour: {:factor, [:cyl]}})
|> Plot.geom_point()
mt
|> Plot.facet_grid(rows: :cyl, scales: :free)
|> render.()
Free space
# If scales and space are free, then the mapping between position
# and values in the data will be the same across all panels. This
# is particularly useful for categorical axes
ggplot(mpg, aes(drv, model)) +
geom_point() +
facet_grid(manufacturer ~ ., scales = "free", space = "free") +
theme(strip.text.y = element_text(angle = 0))
Not in Giggly yet: space, which sizes each panel in proportion to its scales.
Margins
A margin is an extra row or column of panels, labelled (all), that holds the data for every
value of a column.
# Margins ----------------------------------------------------------
# Margins can be specified logically (all yes or all no) or for specific
# variables as (character) variable names
mg <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
mg + facet_grid(vs + am ~ gear, margins = TRUE)
mg + facet_grid(vs + am ~ gear, margins = "am")
# when margins are made over "vs", since the facets for "am" vary
# within the values of "vs", the marginal facet for "vs" is also
# a margin over "am".
mg + facet_grid(vs + am ~ gear, margins = "vs")
Not in Giggly yet: margins.