forked from wch/rgcookbook
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ch11.Rmd
252 lines (172 loc) · 9.02 KB
/
ch11.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
---
output:
bookdown::html_document2:
fig_caption: yes
editor_options:
chunk_output_type: console
---
```{r echo = FALSE, cache = FALSE}
# This block needs cache=FALSE to set fig.width and fig.height, and have those
# persist across cached builds.
source("utils.R", local = TRUE)
knitr::opts_chunk$set(fig.width=6, fig.height=6)
```
Facets {#CHAPTER-FACET}
======
One of the most useful techniques in data visualization is rendering groups of data alongside each other, making it easy to compare the groups. With ggplot2, one way to do this is by mapping a discrete variable to an aesthetic, like *x* `position`, `color`, or `shape.` Another way of doing this is to create a subplot for each group and draw the subplots side by side.
These kinds of plots are known as *Trellis* displays. They're implemented in the lattice package as well as in the ggplot2 package. In ggplot2, they're called *facets*. In this chapter I'll explain how to use them.
Splitting Data into Subplots with Facets {#RECIPE-FACET-BASIC}
----------------------------------------
### Problem
You want to plot subsets of your data in separate panels.
### Solution
Use `facet_grid()` or `facet_wrap()`, and specify the variables on which to split.
With `facet_grid()`, you can specify a variable to split the data into vertical subpanels, and another variable to split it into horizontal subpanels (Figure \@ref(fig:FIG-FACET-FACET-GRID)):
```{r eval=FALSE}
# Create the base plot
mpg_plot <- ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point()
# Faceted by cyl, in horizontally arranged subpanels
mpg_plot +
facet_grid(. ~ cyl)
# Faceted by drv, in vertically arranged subpanels
mpg_plot +
facet_grid(drv ~ .)
# Split by drv (vertical) and cyl (horizontal)
mpg_plot +
facet_grid(drv ~ cyl)
```
```{r FIG-FACET-FACET-GRID, echo=FALSE, fig.cap="Faceting horizontally by cyl (top); faceting vertically by dev (left); faceting in both directions, with both variables (bottom right)", fig.width=8, fig.height=6}
# Put together the above plots with patchwork
mpg_plot <- ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point()
p1 <- mpg_plot +
facet_grid(. ~ cyl)
p2 <- mpg_plot +
facet_grid(drv ~ .)
p3 <- mpg_plot +
facet_grid(drv ~ cyl)
library(patchwork)
plot_spacer() + plot_spacer() + p1 +
plot_spacer() + plot_spacer() + plot_spacer() +
p2 + plot_spacer() + p3 +
plot_layout(heights = c(12, 1, 40), widths = c(10, 1, 50))
```
With `facet_wrap()`, the subplots are laid out horizontally and wrap around, like words on a page, as in Figure \@ref(fig:FIG-FACET-FACET-WRAP):
(ref:cap-FIG-FACET-FACET-WRAP) A scatter plot with `facet_wrap()` on class
```{r FIG-FACET-FACET-WRAP, fig.cap="(ref:cap-FIG-FACET-FACET-WRAP)"}
# Facet on class
# Note that there is nothing before the tilde
mpg_plot +
facet_wrap( ~ class)
```
### Discussion
With `facet_wrap()`, the default is to use the same number of rows and columns. In Figure \@ref(fig:FIG-FACET-FACET-WRAP), there were seven facets, and they fit into a 3×3 square. To change this, you can pass a value for `nrow` or `ncol`:
```{r, eval=FALSE}
# These will have the same result: 2 rows and 4 cols
mpg_plot +
facet_wrap( ~ class, nrow = 2)
mpg_plot +
facet_wrap( ~ class, ncol = 4)
```
The choice of faceting direction depends on the kind of comparison you would like to encourage. For example, if you want to compare heights of bars, it's useful to make the facets go horizontally. If, on the other hand, you want to compare the horizontal distribution of histograms, it makes sense to make the facets go vertically.
Sometimes both kinds of comparisons are important -- there may not be a clear answer as to which faceting direction is best. It may turn out that displaying the groups in a single plot by mapping the grouping variable to an aesthetic like color works better than using facets. In these situations, you'll have to rely on your judgment.
Using Facets with Different Axes {#RECIPE-FACET-FREE}
--------------------------------
### Problem
You want subplots with different ranges or items on their axes.
### Solution
Set the scales to `"free_x"`, `"free_y"`, or `"free"` (Figure \@ref(fig:FIG-FACET-FACET-FREE)):
```{r FIG-FACET-FACET-FREE, fig.cap="With free y scales (top); With free x and y scales (bottom)", fig.width=6, fig.height=4}
# Create the base plot
mpg_plot <- ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point()
# With free y scales
mpg_plot +
facet_grid(drv ~ cyl, scales = "free_y")
# With free x and y scales
mpg_plot +
facet_grid(drv ~ cyl, scales = "free")
```
### Discussion
Each row of subplots has its own *y* range when free *y* scales are used; the same applies to columns when free *x* scales are used.
It's not possible to directly set the range of each row or column, but you can control the ranges by dropping unwanted data (to reduce the ranges), or by adding `geom_blank()` (to expand the ranges).
### See Also
See Recipe \@ref(RECIPE-BAR-GRAPH-DOT-PLOT) for an example of faceting with free scales and a discrete axis.
Changing the Text of Facet Labels {#RECIPE-FACET-LABEL-TEXT}
---------------------------------
### Problem
You want to change the text of facet labels.
### Solution
Change the names of the factor levels (Figure \@ref(fig:FIG-FACET-LABEL-TEXT)):
```{r, eval=FALSE}
library(dplyr)
# Make a modified copy of the original data
mpg_mod <- mpg %>%
# Rename 4 to 4wd, f to Front, r to Rear
mutate(drv = recode(drv, "4" = "4wd", "f" = "Front", "r" = "Rear"))
# Plot the new data
ggplot(mpg_mod, aes(x = displ, y = hwy)) +
geom_point() +
facet_grid(drv ~ .)
```
```{r FIG-FACET-LABEL-TEXT, echo=FALSE, fig.show="hold", fig.cap="Default facet labels (left); Modified facet labels (right)", fig.width=2.5}
library(dplyr)
# Left plot (unmodified labels)
ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point() +
facet_grid(drv ~ .)
# Right plot (modified labels)
mpg_mod <- mpg %>%
# Rename 4 to 4wd, f to Front, r to Rear
mutate(drv = recode(drv, "4" = "4wd", "f" = "Front", "r" = "Rear"))
ggplot(mpg_mod, aes(x = displ, y = hwy)) +
geom_point() +
facet_grid(drv ~ .)
```
### Discussion
Unlike with scales where you can set the labels, to set facet labels you must change the data values. Also, at the time of this writing, there is no way to show the name of the faceting variable as a header for the facets, so it can be useful to use descriptive facet labels.
With `facet_grid()` but not `facet_wrap()`, at this time), it's possible to use a labeller function to set the labels. The labeller function `label_both()` will print out both the name of the variable and the value of the variable in each facet (Figure \@ref(fig:FIG-FACET-LABEL-TEXT-LABELLER), left):
```{r FIG-FACET-LABEL-TEXT-LABELLER-1, eval=FALSE}
ggplot(mpg_mod, aes(x = displ, y = hwy)) +
geom_point() +
facet_grid(drv ~ ., labeller = label_both)
```
Another useful labeller is label_parsed(), which takes strings and treats them as R math expressions (Figure \@ref(fig:FIG-FACET-LABEL-TEXT-LABELLER), right):
```{r FIG-FACET-LABEL-TEXT-LABELLER-2, eval=FALSE}
# Make a modified copy of the original data
mpg_mod <- mpg %>%
mutate(drv = recode(drv,
"4" = "4^{wd}",
"f" = "- Front %.% e^{pi * i}",
"r" = "4^{wd} - Front"
))
ggplot(mpg_mod, aes(x = displ, y = hwy)) +
geom_point() +
facet_grid(drv ~ ., labeller = label_parsed)
```
(ref:cap-FIG-FACET-LABEL-TEXT-LABELLER) With `label_both()` (left); With `label_parsed()` for mathematical expressions (right)
```{r FIG-FACET-LABEL-TEXT-LABELLER, ref.label=c("FIG-FACET-LABEL-TEXT-LABELLER-1", "FIG-FACET-LABEL-TEXT-LABELLER-2"), echo=FALSE, fig.cap="(ref:cap-FIG-FACET-LABEL-TEXT-LABELLER)", fig.width=2.5}
```
### See Also
See Recipe \@ref(RECIPE-DATAPREP-FACTOR-RENAME) for more on renaming factor levels. If the faceting variable is not a factor but a character vector, changing the values is somewhat different. See Recipe \@ref(RECIPE-DATAPREP-CHARACTER-RENAME) for information on renaming items in character vectors.
Changing the Appearance of Facet Labels and Headers {#RECIPE-FACET-LABEL-APPEARANCE}
---------------------------------------------------
### Problem
You want to change the appearance of facet labels and headers.
### Solution
With the theming system, set `strip.text` to control the text appearance and `strip.background` to control the background appearance (Figure \@ref(fig:FIG-FACET-LABEL-APPEARANCE)):
```{r FIG-FACET-LABEL-APPEARANCE, fig.cap="Customized appearance for facet labels", fig.height=3}
library(gcookbook) # Load gcookbook for the cabbage_exp data set
ggplot(cabbage_exp, aes(x = Cultivar, y = Weight)) +
geom_col() +
facet_grid(. ~ Date) +
theme(
strip.text = element_text(face = "bold", size = rel(1.5)),
strip.background = element_rect(fill = "lightblue", colour = "black", size = 1)
)
```
### Discussion
Using `rel(1.5)` makes the label text 1.5 times the size of the base text size for the theme. Using `size = 1` for the background makes the outline of the facets 1 mm thick.
### See Also
For more on how the theme system works, see Recipes Recipe \@ref(RECIPE-APPEARANCE-THEME) and Recipe \@ref(RECIPE-APPEARANCE-THEME-MODIFY).