ggplot(chic, aes(x = date, y = temp)) +
geom_point(color = "orangered", alpha = .3) +
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) +
labs(x = "Year", y = "Temperature (°F)") +
facet_grid(year ~ season)
9 Working with Multi-Panel Plots
The {ggplot2}
package offers two handy functions for creating multi-panel plots, called facets. They are related but have slight differences: facet_wrap
creates a ribbon of plots based on a single variable, while facet_grid
spans a grid of plots based on two variables.
9.1 Create a Grid of Small Multiples Based on Two Variables
When dealing with two variables, facet_grid
is the appropriate choice. In this function, the order of the variables determines the number of rows and columns in the grid:
To switch from a row-based arrangement to a column-based one, you can modify facet_grid(year ~ season)
to facet_grid(season ~ year)
.
9.2 Create Small Multiples Based on One Variable
facet_wrap
creates a facet of a single variable, specified with a tilde in front: facet_wrap(~ variable)
. The appearance of these subplots is determined by the arguments ncol
and nrow
:
<-
g ggplot(chic, aes(x = date, y = temp)) +
geom_point(color = "chartreuse4", alpha = .3) +
labs(x = "Year", y = "Temperature (°F)") +
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1))
+ facet_wrap(~ year) g
Accordingly, you can arrange the plots as you like, instead as a matrix in one row…
+ facet_wrap(~ year, nrow = 1) g
… or even as a asymmetric grid of plots:
+ facet_wrap(~ year, ncol = 3) + theme(axis.title.x = element_text(hjust = .15)) g
9.3 Allow Axes to Roam Free
The default for multi-panel plots in {ggplot2}
is to use equivalent scales in each panel. But sometimes you want to allow a panels own data to determine the scale. This is often not a good idea since it may give your user the wrong impression about the data. But sometimes it is indeed useful and to do this you can set scales = "free"
:
+ facet_wrap(~ year, nrow = 2, scales = "free") g
Note that both, x and y axes differ in their range!
9.3.0.1 Use facet_wrap
with Two Variables
The function facet_wrap
can also take two variables:
+ facet_wrap(year ~ season, nrow = 4, scales = "free_x") g
When using facet_wrap
you are still able to control the grid design: you can rearrange the number of plots per row and column and you can also let all axes roam free. In contrast, facet_grid
will also take a free
argument but will only let it roam free per column or row:
+ facet_grid(year ~ season, scales = "free_x") g
9.4 Modify Style of Strip Texts
By using theme
, you can modify the appearance of the strip text (i.e. the title for each facet) and the strip text boxes:
+ facet_wrap(~ year, nrow = 1, scales = "free_x") +
g theme(strip.text = element_text(face = "bold", color = "chartreuse4",
hjust = 0, size = 20),
strip.background = element_rect(fill = "chartreuse3", linetype = "dotted"))
The following two functions adapted from this answer by Claus Wilke, the author of the {ggtext}
package, allow to highlight specific labels in combination with element_textbox()
that is provided by {ggtext}
.
library(ggtext)
library(purrr) ## for %||%
<- function(..., hi.labels = NULL, hi.fill = NULL,
element_textbox_highlight hi.col = NULL, hi.box.col = NULL, hi.family = NULL) {
structure(
c(element_textbox(...),
list(hi.labels = hi.labels, hi.fill = hi.fill, hi.col = hi.col, hi.box.col = hi.box.col, hi.family = hi.family)
),class = c("element_textbox_highlight", "element_textbox", "element_text", "element")
)
}
<- function(element, label = "", ...) {
element_grob.element_textbox_highlight if (label %in% element$hi.labels) {
$fill <- element$hi.fill %||% element$fill
element$colour <- element$hi.col %||% element$colour
element$box.colour <- element$hi.box.col %||% element$box.colour
element$family <- element$hi.family %||% element$family
element
}NextMethod()
}
Now you can use it and specify for example all striptexts:
+ facet_wrap(year ~ season, nrow = 4, scales = "free_x") +
g theme(
strip.background = element_blank(),
strip.text = element_textbox_highlight(
family = "Playfair Display", size = 12, face = "bold",
fill = "white", box.color = "chartreuse4", color = "chartreuse4",
halign = .5, linetype = 1, r = unit(5, "pt"), width = unit(1, "npc"),
padding = margin(5, 0, 3, 0), margin = margin(0, 1, 3, 1),
hi.labels = c("1997", "1998", "1999", "2000"),
hi.fill = "chartreuse4", hi.box.col = "black", hi.col = "white"
) )
ggplot(chic, aes(x = date, y = temp)) +
geom_point(aes(color = season == "Summer"), alpha = .3) +
labs(x = "Year", y = "Temperature (°F)") +
facet_wrap(~ season, nrow = 1) +
scale_color_manual(values = c("gray40", "firebrick"), guide = "none") +
theme(
axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1),
strip.background = element_blank(),
strip.text = element_textbox_highlight(
size = 12, face = "bold",
fill = "white", box.color = "white", color = "gray40",
halign = .5, linetype = 1, r = unit(0, "pt"), width = unit(1, "npc"),
padding = margin(2, 0, 1, 0), margin = margin(0, 1, 3, 1),
hi.labels = "Summer", hi.family = "Bangers",
hi.fill = "firebrick", hi.box.col = "firebrick", hi.col = "white"
) )
9.5 Create a Panel of Different Plots
There are several ways how plots can be combined. The easiest approach in my opinion is the {patchwork}
package by Thomas Lin Pedersen:
<- ggplot(chic, aes(x = date, y = temp,
p1 color = season)) +
geom_point() +
geom_rug() +
labs(x = "Year", y = "Temperature (°F)")
<- ggplot(chic, aes(x = date, y = o3)) +
p2 geom_line(color = "gray") +
geom_point(color = "darkorange2") +
labs(x = "Year", y = "Ozone")
library(patchwork)
+ p2 p1
We can change the order by “dividing” both plots (and note the alignment even though one has a legend and one doesn’t!):
/ p2 p1
And also nested plots are possible!
+ p2) / p1 (g
(Note the alignment of the plots even though only one plot includes a legend.)
Alternatively, the {cowplot}
package by Claus Wilke provides the functionality to combine multiple plots (and lots of other good utilities):
library(cowplot)
Attaching package: 'cowplot'
The following object is masked from 'package:patchwork':
align_plots
The following object is masked from 'package:lubridate':
stamp
plot_grid(plot_grid(g, p1), p2, ncol = 1)
… and so does the {gridExtra}
package as well:
library(gridExtra)
Attaching package: 'gridExtra'
The following object is masked from 'package:dplyr':
combine
grid.arrange(g, p1, p2,
layout_matrix = rbind(c(1, 2), c(3, 3)))
The same idea of defining a layout can be used with {patchwork}
which allows creating complex compositions:
<- "
layout AABBBB#
AACCDDE
##CCDD#
##CC###
"
+ p1 + p1 + g + p2 +
p2 plot_layout(design = layout)