-
Notifications
You must be signed in to change notification settings - Fork 175
/
ch07.Rmd
497 lines (332 loc) · 21.6 KB
/
ch07.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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
---
output:
bookdown::html_document2:
fig_caption: yes
editor_options:
chunk_output_type: console
---
```{r echo = FALSE, cache = FALSE}
source("utils.R", local = TRUE)
```
Annotations {#CHAPTER-ANNOTATE}
===========
Displaying just your data usually isn't enough -- there's all sorts of other information that can help the viewer interpret the data. In addition to the standard repertoire of axis labels, tick marks, and legends, you can also add individual graphical or text elements to your plot. These elements can be used to add extra contextual information, highlight an area of the plot, or add some descriptive text about the data.
Adding Text Annotations {#RECIPE-ANNOTATE-TEXT}
-----------------------
### Problem
You want to add a text annotation to a plot.
### Solution
Use `annotate()` and a text geom (Figure \@ref(fig:FIG-ANNOTATE-TEXT)):
```{r FIG-ANNOTATE-TEXT, fig.cap="Text annotations", fig.width=4, fig.height=4}
p <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
geom_point()
p +
annotate("text", x = 3, y = 48, label = "Group 1") +
annotate("text", x = 4.5, y = 66, label = "Group 2")
```
### Discussion
The `annotate()` function can be used to add any type of geometric object. In this case, we used `geom = "text"`.
Other text properties can be specified, as shown in Figure \@ref(fig:FIG-ANNOTATE-TEXT-PROPERTIES):
```{r FIG-ANNOTATE-TEXT-PROPERTIES, fig.cap="Modified text properties", fig.width=4, fig.height=4}
p +
annotate("text", x = 3, y = 48, label = "Group 1",
family = "serif", fontface = "italic", colour = "darkred", size = 3) +
annotate("text", x = 4.5, y = 66, label = "Group 2",
family = "serif", fontface = "italic", colour = "darkred", size = 3)
```
Be careful not to use `geom_text()` when you want to add individual text objects. While `annotate(geom = "text")` will add a single text object to the plot, `geom_text()` will create many text objects based on the data, as discussed in Recipe \@ref(RECIPE-SCATTER-LABELS).
If you use `geom_text()`, the text will be heavily overplotted on the same location, with one copy per data point:
```{r FIG-ANNOTATE-TEXT-GEOM-OVERPLOT, fig.cap="Overplotting one of the labels -- both should be 90\\% transparent", fig.width=4, fig.height=4}
p +
# Normal
annotate("text", x = 3, y = 48, label = "Group 1", alpha = .1) +
# Overplotted
geom_text(x = 4.5, y = 66, label = "Group 2", alpha = .1)
```
In Figure \@ref(fig:FIG-ANNOTATE-TEXT-GEOM-OVERPLOT), each text label is 90% transparent, making it clear which one is overplotted. The overplotting can lead to output with aliased (jagged) edges when outputting to a bitmap.
If the axes are continuous, you can use the special values `Inf` and `-Inf` to place text annotations at the edge of the plotting area, as shown in Figure \@ref(fig:FIG-ANNOTATE-TEXT-EDGE). You will also need to adjust the position of the text relative to the corner using `hjust` and `vjust` -- if you leave them at their default values, the text will be centered on the edge. It may take a little experimentation with these values to get the text positioned to your liking:
```{r FIG-ANNOTATE-TEXT-EDGE, fig.cap="Text positioned at the edge of the plotting area", fig.width=4, fig.height=4}
p +
annotate("text", x = -Inf, y = Inf, label = "Upper left", hjust = -.2, vjust = 2) +
annotate("text", x = mean(range(faithful$eruptions)), y = -Inf, vjust = -0.4,
label = "Bottom middle")
```
### See Also
See Recipe \@ref(RECIPE-SCATTER-LABELS) for making a scatter plot with text.
For more on controlling the appearance of the text, see Recipe \@ref(RECIPE-APPEARANCE-TEXT-APPEARANCE).
Using Mathematical Expressions in Annotations {#RECIPE-ANNOTATE-TEXT-MATH}
---------------------------------------------
### Problem
You want to add a text annotation with mathematical notation.
### Solution
Use `annotate(geom = "text")` with `parse = TRUE` (Figure \@ref(fig:FIG-ANNOTATE-TEXT-MATH)):
```{r FIG-ANNOTATE-TEXT-MATH, fig.cap="Annotation with mathematical expressions"}
# A normal curve
p <- ggplot(data.frame(x = c(-3,3)), aes(x = x)) +
stat_function(fun = dnorm)
p +
annotate("text", x = 2, y = 0.3, parse = TRUE,
label = "frac(1, sqrt(2 * pi)) * e ^ {-x^2 / 2}")
```
### Discussion
Mathematical expressions made with text geoms using `parse = TRUE` in ggplot2 have a format similar to those made with `plotmath` and `expression` in base R, except that they are stored as strings, rather than as expression objects.
To mix regular text with expressions, use single quotes within double quotes (or vice versa) to mark the plain-text parts. Each block of text enclosed by the inner quotes is treated as a variable in a mathematical expression. Bear in mind that, in R's syntax for mathematical expressions, you can't simply put a variable right next to another without something else in between. To display two variables next to each other, as in Figure \@ref(fig:FIG-ANNOTATE-TEXT-MATH-MIX), put a `*` operator between them; when displayed in a graphic, this is treated as an invisible multiplication sign (for a visible multiplication sign, use `%*%`):
```{r FIG-ANNOTATE-TEXT-MATH-MIX, fig.cap="Mathematical expression with regular text"}
p +
annotate("text", x = 0, y = 0.05, parse = TRUE, size = 4,
label = "'Function: ' * y==frac(1, sqrt(2*pi)) * e^{-x^2/2}")
```
### See Also
See `?plotmath` for many examples of mathematical expressions, and `?demo(plotmath)` for graphical examples of mathematical expressions.
See Recipe \@ref(RECIPE-SCATTER-FITLINES-TEXT) for adding regression coefficients to a graph.
For using other fonts in mathematical expressions, see Recipe \@ref(RECIPE-OUTPUT-FONTS-PDF).
Adding Lines {#RECIPE-ANNOTATE-LINES}
------------
### Problem
You want to add lines to a plot.
### Solution
For horizontal and vertical lines, use `geom_hline()` and `geom_vline()`, and for angled lines, use `geom_abline()` (Figure \@ref(fig:FIG-ANNOTATE-LINES)). For this example, we'll use the `heightweight` data set:
```{r FIG-ANNOTATE-LINES, fig.show="hold", fig.cap="horizontal and vertical lines (left); angled line (right)"}
library(gcookbook) # Load gcookbook for the heightweight data set
hw_plot <- ggplot(heightweight, aes(x = ageYear, y = heightIn, colour = sex)) +
geom_point()
# Add horizontal and vertical lines
hw_plot +
geom_hline(yintercept = 60) +
geom_vline(xintercept = 14)
# Add angled line
hw_plot +
geom_abline(intercept = 37.4, slope = 1.75)
```
### Discussion
The previous examples demonstrate setting the positions of the lines manually, resulting in one line drawn for each geom added. It is also possible to *map* values from the data to `xintercept`, `yintercept`, and so on, and even draw them from another data frame.
Here we'll take the average height for males and females and store it in a data frame, `hw_means`. Then we'll draw a horizontal line for each, and set the `linetype` and `size` (Figure \@ref(fig:FIG-ANNOTATE-LINES-MULTI)):
```{r FIG-ANNOTATE-LINES-MULTI, fig.cap="Multiple lines, drawn at the mean of each group"}
library(dplyr)
hw_means <- heightweight %>%
group_by(sex) %>%
summarise(heightIn = mean(heightIn))
hw_means
hw_plot +
geom_hline(
data = hw_means,
aes(yintercept = heightIn, colour = sex),
linetype = "dashed",
size = 1
)
```
If one of the axes is discrete rather than continuous, you can't specify the intercepts as just a character string -- they must still be specified as numbers. If the axis represents a factor, the first level has a numeric value of 1, the second level has a value of 2, and so on. You can specify the numerical intercept manually, or calculate the numerical value using `which(levels(...))` (Figure \@ref(fig:FIG-ANNOTATE-LINES-FACTOR)):
```{r FIG-ANNOTATE-LINES-FACTOR, fig.cap="Lines with a discrete axis"}
pg_plot <- ggplot(PlantGrowth, aes(x = group, y = weight)) +
geom_point()
pg_plot +
geom_vline(xintercept = 2)
pg_plot +
geom_vline(xintercept = which(levels(PlantGrowth$group) == "ctrl"))
```
> **Note**
>
> You may have noticed that adding lines differs from adding other annotations. Instead of using the `annotate()` function, we've used `geom_hline()` and friends. This is because old versions of ggplot2 didn't have the `annotate()` function. The line geoms had code to handle the special cases where they were used to add a single line, and changing it would break backward compatibility.
### See Also
For adding regression lines, see Recipes Recipe \@ref(RECIPE-SCATTER-FITLINES) and Recipe \@ref(RECIPE-SCATTER-FITLINES-MODEL).
Lines are often used to indicate summarized information about data. See Recipe \@ref(RECIPE-DATAPREP-SUMMARIZE) for more on how to summarize data by groups.
Adding Line Segments and Arrows {#RECIPE-ANNOTATE-SEGMENT}
-------------------------------
### Problem
You want to add line segments or arrows to a plot.
### Solution
Use `annotate("segment")`. In this example, we'll use the climate data set and use a subset of data from the Berkeley source (Figure \@ref(fig:FIG-ANNOTATE-SEGMENT)):
```{r FIG-ANNOTATE-SEGMENT, fig.cap="Line segment annotation", fig.width=10, fig.height=3}
library(gcookbook) # Load gcookbook for the climate data set
p <- ggplot(filter(climate, Source == "Berkeley"), aes(x = Year, y = Anomaly10y)) +
geom_line()
p +
annotate("segment", x = 1950, xend = 1980, y = -.25, yend = -.25)
```
### Discussion
It's possible to add arrowheads or flat ends to the line segments, using `arrow()` from the grid package. In this example, we'll do both (Figure \@ref(fig:FIG-ANNOTATE-SEGMENT-ARROWS)):
```{r FIG-ANNOTATE-SEGMENT-ARROWS, fig.cap="Line segments with arrow heads", fig.width=10, fig.height=3}
library(grid)
p +
annotate("segment", x = 1850, xend = 1820, y = -.8, yend = -.95,
colour = "blue", size = 2, arrow = arrow()) +
annotate("segment", x = 1950, xend = 1980, y = -.25, yend = -.25,
arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm")))
```
The default angle is 30, and the default length of the arrowhead lines is 0.2 inches.
If one or both axes are discrete, the *x* and *y* positions are such that the categorical items have coordinate values 1, 2, 3, and so on.
### See Also
For more information about the parameters for drawing arrows, load the grid package and see `?arrow`.
Adding a Shaded Rectangle {#RECIPE-ANNOTATE-RECT}
-------------------------
### Problem
You want to add a shaded region.
### Solution
Use `annotate("rect")` (Figure \@ref(fig:FIG-ANNOTATE-RECT)):
```{r FIG-ANNOTATE-RECT, fig.cap="A shaded rectangle", fig.width=10, fig.height=3}
library(gcookbook) # Load gcookbook for the climate data set
p <- ggplot(filter(climate, Source == "Berkeley"), aes(x = Year, y = Anomaly10y)) +
geom_line()
p +
annotate("rect", xmin = 1950, xmax = 1980, ymin = -1, ymax = 1,
alpha = .1,fill = "blue")
```
### Discussion
Each layer is drawn in the order that it's added to the ggplot object, so in the preceding example, the rectangle is drawn on top of the line. It's not a problem in that case, but if you'd like to have the line above the rectangle, add the rectangle first, and then the line.
Any geom can be used with `annotate()`, as long as you pass in the proper parameters. In this case, `geom_rect()` requires min and max values for x and y.
Highlighting an Item {#RECIPE-ANNOTATE-HIGHLIGHT}
--------------------
### Problem
You want to change the color of an item to make it stand out.
### Solution
To highlight one or more items, create a new column in the data and map it to the color. In this example, we'll create a copy of the PlantGrowth data set called `pg_mod` and create a new column, `hl`, which is set to `no` if the case was in the control group or treatment 1 group, and set to `yes` if the case was in the treatment 2 group:
```{r}
library(dplyr)
pg_mod <- PlantGrowth %>%
mutate(hl = recode(group, "ctrl" = "no", "trt1" = "no", "trt2" = "yes"))
```
Then we'll plot this data with specified colors, and hiding the legend (Figure \@ref(fig:FIG-ANNOTATE-HIGHLIGHT)):
```{r FIG-ANNOTATE-HIGHLIGHT, fig.cap="Highlighting one item", fig.width=4, fig.height=4}
ggplot(pg_mod, aes(x = group, y = weight, fill = hl)) +
geom_boxplot() +
scale_fill_manual(values = c("grey85", "#FFDDCC"), guide = FALSE)
```
### Discussion
If you have a small number of items, as in this example, instead of creating a new column you could use the original one and specify the colors for every level of that variable. For example, the following code will use the group column from `PlantGrowth` and manually set the colors for each of the three levels. The result will appear the same as with the preceding code:
```{r eval=FALSE}
ggplot(PlantGrowth, aes(x = group, y = weight, fill = group)) +
geom_boxplot() +
scale_fill_manual(values = c("grey85", "grey85", "#FFDDCC"), guide = FALSE)
```
### See Also
See Chapter \@ref(CHAPTER-COLORS) for more information about specifying colors.
For more information about removing the legend, see Recipe \@ref(RECIPE-LEGEND-REMOVE).
Adding Error Bars {#RECIPE-ANNOTATE-ERROR-BAR}
-----------------
### Problem
You want to add error bars to a graph.
### Solution
Use `geom_errorbar()` and map variables to the values for `ymin` and `ymax`. Adding the error bars is done the same way for bar graphs and line graphs, as shown in Figure \@ref(fig:FIG-ANNOTATE-ERRORBAR-BASIC) (notice that default *y* range is different for bars and lines, though):
```{r FIG-ANNOTATE-ERRORBAR-BASIC, fig.show="hold", fig.cap="error bars on a bar graph (left); on a line graph (right)"}
library(gcookbook) # Load gcookbook for the cabbage_exp data set
library(dplyr)
# Take a subset of the cabbage_exp data for this example
ce_mod <- cabbage_exp %>%
filter(Cultivar == "c39")
# With a bar graph
ggplot(ce_mod, aes(x = Date, y = Weight)) +
geom_col(fill = "white", colour = "black") +
geom_errorbar(aes(ymin = Weight - se, ymax = Weight + se), width = .2)
# With a line graph
ggplot(ce_mod, aes(x = Date, y = Weight)) +
geom_line(aes(group = 1)) +
geom_point(size = 4) +
geom_errorbar(aes(ymin = Weight - se, ymax = Weight + se), width = .2)
```
### Discussion
In this example, the data already has values for the standard error of the mean (`se`), which we'll use for the error bars (it also has values for the standard deviation, `sd`, but we're not using that here):
```{r}
ce_mod
```
To get the values for `ymax` and `ymin`, we took the y variable, `Weight`, and added/subtracted `se`.
We also specified the width of the ends of the error bars, with `width = .2`. It's best to play around with this to find a value that looks good. If you don't set the width, the error bars will be very wide, spanning all the space between items on the x-axis.
For a bar graph with groups of bars, the error bars must also be *dodged*; otherwise, they'll have the exact same *x* coordinate and won't line up with the bars. (See Recipe \@ref(RECIPE-BAR-GRAPH-GROUPED-BAR) for more information about grouped bars and dodging.)
We'll work with the full `cabbage_exp` data set this time:
```{r}
cabbage_exp
```
The default dodge width for `geom_bar()` is 0.9, and you'll have to tell the error bars to be dodged the same width. If you don't specify the dodge width, it will default to dodging by the width of the error bars, which is usually less than the width of the bars (Figure \@ref(fig:FIG-ANNOTATE-ERRORBAR-GROUP-BAR)):
```{r FIG-ANNOTATE-ERRORBAR-GROUP-BAR, fig.show="hold", fig.cap="error bars on a grouped bar graph without dodging width specified (left); with dodging width specified (right)"}
# Bad: dodge width not specified
ggplot(cabbage_exp, aes(x = Date, y = Weight, fill = Cultivar)) +
geom_col(position = "dodge") +
geom_errorbar(aes(ymin = Weight - se, ymax = Weight + se),
position = "dodge", width = .2)
# Good: dodge width set to same as bar width (0.9)
ggplot(cabbage_exp, aes(x = Date, y = Weight, fill = Cultivar)) +
geom_col(position = "dodge") +
geom_errorbar(aes(ymin = Weight - se, ymax = Weight + se),
position = position_dodge(0.9), width = .2)
```
> **Note**
>
> Notice that we used `position = "dodge"`, which is shorthand for `position = position_dodge()`, in the first version. But to pass a specific value, we have to spell it out, as in `position_dodge(0.9)`.
For line graphs, if the error bars are a different color than the lines and points, you should draw the error bars first, so that they are underneath the points and lines. Otherwise the error bars will be drawn on top of the points and lines, which won't look right.
Additionally, you should dodge all the geometric elements so that they will align with the error bars, as shown in Figure \@ref(fig:FIG-ANNOTATE-ERRORBAR-GROUP-LINE-DODGE):
```{r FIG-ANNOTATE-ERRORBAR-GROUP-LINE-DODGE, fig.show="hold", fig.cap="Error bars on a line graph, dodged so they don't overlap"}
pd <- position_dodge(.3) # Save the dodge spec because we use it repeatedly
ggplot(cabbage_exp, aes(x = Date, y = Weight, colour = Cultivar, group = Cultivar)) +
geom_errorbar(
aes(ymin = Weight - se, ymax = Weight + se),
width = .2,
size = 0.25,
colour = "black",
position = pd
) +
geom_line(position = pd) +
geom_point(position = pd, size = 2.5)
# Thinner error bar lines with size = 0.25, and larger points with size = 2.5
```
Notice that we set `colour = "black"` to make the error bars black; otherwise, they would inherit `colour`. We also made sure the `Cultivar` was used as a grouping variable by mapping it to group.
When a discrete variable is *mapped* to an aesthetic like colour or fill (as in the case of the bars), that variable is used for grouping the data. But by *setting* the colour of the error bars, we made it so that the variable for colour was not used for grouping, and we needed some other way to inform ggplot that the two data entries at each *x* were in different groups so that they would be dodged.
### See Also
See Recipe \@ref(RECIPE-BAR-GRAPH-GROUPED-BAR) for more about creating grouped bar graphs, and Recipe \@ref(RECIPE-LINE-GRAPH-MULTIPLE-LINE) for more about creating line graphs with multiple lines.
See Recipe \@ref(RECIPE-DATAPREP-SUMMARIZE-SE) for calculating summaries with means, standard deviations, standard errors, and confidence intervals.
See Recipe \@ref(RECIPE-LINE-GRAPH-REGION) for adding a confidence region when the data has a higher density along the x-axis.
Adding Annotations to Individual Facets {#RECIPE-ANNOTATE-FACET}
---------------------------------------
### Problem
You want to add annotations to each facet in a plot.
### Solution
Create a new data frame with the faceting variable(s), and a value to use in each facet. Then use `geom_text()` with the new data frame (Figure \@ref(fig:FIG-ANNOTATE-FACET)):
```{r FIG-ANNOTATE-FACET, fig.show="hold", fig.cap="Top: different annotations in each facet; bottom: the same annotation in each facet", fig.width=8, fig.height=3}
# Create the base plot
mpg_plot <- ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point() +
facet_grid(. ~ drv)
# A data frame with labels for each facet
f_labels <- data.frame(drv = c("4", "f", "r"), label = c("4wd", "Front", "Rear"))
mpg_plot +
geom_text(x = 6, y = 40, aes(label = label), data = f_labels)
# If you use annotate(), the label will appear in all facets
mpg_plot +
annotate("text", x = 6, y = 42, label = "label text")
```
### Discussion
This method can be used to display information about the data in each facet, as shown in Figure \@ref(fig:FIG-ANNOTATE-FACET-MATH). For example, in each facet we can show linear regression lines, the formula for each line, and the *r^2^* value. To do this, we'll write a function that takes a data frame and returns another data frame containing a string for a regression equation, and a string for the *r^2^* value. Then we'll use dplyr's `do()` function to apply that function to each group of the data:
```{r FIG-ANNOTATE-FACET-MATH, fig.cap="Annotations in each facet with information about the data", fig.width=8, fig.height=3}
# This function returns a data frame with strings representing the regression
# equation, and the r^2 value.
# These strings will be treated as R math expressions
lm_labels <- function(dat) {
mod <- lm(hwy ~ displ, data = dat)
formula <- sprintf("italic(y) == %.2f %+.2f * italic(x)",
round(coef(mod)[1], 2), round(coef(mod)[2], 2))
r <- cor(dat$displ, dat$hwy)
r2 <- sprintf("italic(R^2) == %.2f", r^2)
data.frame(formula = formula, r2 = r2, stringsAsFactors = FALSE)
}
library(dplyr)
labels <- mpg %>%
group_by(drv) %>%
do(lm_labels(.))
labels
# Plot with formula and R^2 values
mpg_plot +
geom_smooth(method = lm, se = FALSE) +
geom_text(data = labels, aes(label = formula), x = 3, y = 40, parse = TRUE, hjust = 0) +
geom_text(x = 3, y = 35, aes(label = r2), data = labels, parse = TRUE, hjust = 0)
```
We needed to write our own function here because generating the linear model and extracting the coefficients requires operating on each subset data frame directly. If you just want to display the r^2^ values, it's possible to do something simpler, by using the `group_by()` and with the `summarise()` function and then passing additional arguments for `summarise()`:
```{r}
# Find r^2 values for each group
labels <- mpg %>%
group_by(drv) %>%
summarise(r2 = cor(displ, hwy)^2)
labels$r2 <- sprintf("italic(R^2) == %.2f", labels$r2)
labels
```
Text geoms aren't the only kind that can be added individually for each facet. Any geom can be used, as long as the input data is structured correctly.
### See Also
See Recipe \@ref(RECIPE-ANNOTATE-TEXT-MATH) for more about using math expressions in plots.
If you want to make prediction lines from your own model objects, instead of having ggplot2 do it for you with `stat_smooth()`, see Recipe \@ref(RECIPE-SCATTER-FITLINES-MODEL-MULTI).