forked from hadley/ggplot2-book
-
Notifications
You must be signed in to change notification settings - Fork 0
/
themes.Rmd
521 lines (402 loc) · 24.1 KB
/
themes.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
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
```{r polishing, include = FALSE}
source("common.R")
```
# Themes {#polishing}
## Introduction
In this chapter you will learn how to use the ggplot2 theme system, which allows you to exercise fine control over the non-data elements of your plot. The theme system does not affect how the data is rendered by geoms, or how it is transformed by scales. Themes don't change the perceptual properties of the plot, but they do help you make the plot aesthetically pleasing or match an existing style guide. Themes give you control over things like fonts, ticks, panel strips, and backgrounds. \index{Themes}
This separation of control into data and non-data parts is quite different from base and lattice graphics. In base and lattice graphics, most functions take a large number of arguments that specify both data and non-data appearance, which makes the functions complicated and harder to learn. ggplot2 takes a different approach: when creating the plot you determine how the data is displayed, then *after* it has been created you can edit every detail of the rendering, using the theming system.
The theming system is composed of four main components:
* Theme __elements__ specify the non-data elements that you can control.
For example, the `plot.title` element controls the appearance of the
plot title; `axis.ticks.x`, the ticks on the x axis; `legend.key.height`,
the height of the keys in the legend.
* Each element is associated with an __element function__, which describes
the visual properties of the element. For example, `element_text()` sets
the font size, colour and face of text elements like `plot.title`.
* The `theme()` function which allows you to override the default theme
elements by calling element functions, like
`theme(plot.title = element_text(colour = "red"))`.
* Complete __themes__, like `theme_grey()` set all of the theme elements to
values designed to work together harmoniously.
For example, imagine you've made the following plot of your data.
`r columns(1, 2/3, 0.75)`
```{r motivation-1}
base <- ggplot(mpg, aes(cty, hwy, color = factor(cyl))) +
geom_jitter() +
geom_abline(colour = "grey50", size = 2)
base
```
It's served its purpose for you: you've learned that `cty` and `hwy` are highly correlated, both are tightly coupled with `cyl`, and that `hwy` is always greater than `cty` (and the difference increases as `cty` increases). Now you want to share the plot with others, perhaps by publishing it in a paper. That requires some changes. First, you need to make sure the plot can stand alone by:
* Improving the axes and legend labels.
* Adding a title for the plot.
* Tweaking the colour scale.
Fortunately you know how to do that already because you've read Section \@ref(titles) and Chapter \@ref(scale-colour):
```{r motivation-2}
labelled <- base +
labs(
x = "City mileage/gallon",
y = "Highway mileage/gallon",
colour = "Cylinders",
title = "Highway and city mileage are highly correlated"
) +
scale_colour_brewer(type = "seq", palette = "Spectral")
labelled
```
Next, you need to make sure the plot matches the style guidelines of your journal:
* The background should be white, not pale grey.
* The legend should be placed inside the plot if there's room.
* Major gridlines should be a pale grey and minor gridlines should be removed.
* The plot title should be 12pt bold text.
In this chapter, you'll learn how to use the theming system to make those changes, as shown below:
```{r motivation-3}
styled <- labelled +
theme_bw() +
theme(
plot.title = element_text(face = "bold", size = 12),
legend.background = element_rect(fill = "white", size = 4, colour = "white"),
legend.justification = c(0, 1),
legend.position = c(0, 1),
axis.ticks = element_line(colour = "grey70", size = 0.2),
panel.grid.major = element_line(colour = "grey70", size = 0.2),
panel.grid.minor = element_blank()
)
styled
```
Finally, the journal wants the figure as a 600 dpi TIFF file. You'll learn the fine details of `ggsave()` in Section \@ref(saving).
## Complete themes {#themes}
ggplot2 comes with a number of built in themes. The most important is `theme_grey()`, the signature ggplot2 theme with a light grey background and white gridlines. The theme is designed to put the data forward while supporting comparisons, following the advice of [@tufte:2006; @brewer:1994; @carr:2002; @carr:1994; @carr:1999]. We can still see the gridlines to aid in the judgement of position [@cleveland:1993a], but they have little visual impact and we can easily 'tune' them out. The grey background gives the plot a similar typographic colour to the text, ensuring that the graphics fit in with the flow of a
document without jumping out with a bright white background. Finally, the grey background creates a continuous field of colour which ensures that the plot is perceived as a single visual entity. \index{Themes!built-in} \indexf{theme\_grey}
There are seven other themes built in to ggplot2 1.1.0:
* `theme_bw()`: a variation on `theme_grey()` that uses a white background
and thin grey grid lines. \indexf{theme\_bw}
* `theme_linedraw()`: A theme with only black lines of various widths on white
backgrounds, reminiscent of a line drawing. \indexf{theme\_linedraw}
* `theme_light()`: similar to `theme_linedraw()` but with light grey lines and
axes, to direct more attention towards the data. \indexf{theme\_light}
* `theme_dark()`: the dark cousin of `theme_light()`, with similar line sizes
but a dark background. Useful to make thin coloured lines pop out.
\indexf{theme\_dark}
* `theme_minimal()`: A minimalistic theme with no background annotations.
\indexf{theme\_minimal}
* `theme_classic()`: A classic-looking theme, with x and y axis lines and no
gridlines. \indexf{theme\_classic}
* `theme_void()`: A completely empty theme. \indexf{theme\_void}
`r columns(3, 3/4)`
```{r built-in}
df <- data.frame(x = 1:3, y = 1:3)
base <- ggplot(df, aes(x, y)) + geom_point()
base + theme_grey() + ggtitle("theme_grey()")
base + theme_bw() + ggtitle("theme_bw()")
base + theme_linedraw() + ggtitle("theme_linedraw()")
```
```{r}
base + theme_light() + ggtitle("theme_light()")
base + theme_dark() + ggtitle("theme_dark()")
base + theme_minimal() + ggtitle("theme_minimal()")
```
```{r}
base + theme_classic() + ggtitle("theme_classic()")
base + theme_void() + ggtitle("theme_void()")
```
All themes have a `base_size` parameter which controls the base font size. The base font size is the size that the axis titles use: the plot title is usually bigger (1.2x), and the tick and strip labels are smaller (0.8x). If you want to control these sizes separately, you'll need to modify the individual elements as described below.
As well as applying themes a plot at a time, you can change the default theme with `theme_set()`. For example, if you really hate the default grey background, run `theme_set(theme_bw())` to use a white background for all plots. \indexf{theme\_set}
You're not limited to the themes built-in to ggplot2. Other packages, like ggthemes by Jeffrey Arnold, add even more. Here are a few of my favourites from ggthemes: \index{ggtheme}
```{r ggtheme, message = FALSE, warning = FALSE}
library(ggthemes)
base + theme_tufte() + ggtitle("theme_tufte()")
base + theme_solarized() + ggtitle("theme_solarized()")
base + theme_excel() + ggtitle("theme_excel()") # ;)
```
The complete themes are a great place to start but don't give you a lot of control. To modify individual elements, you need to use `theme()` to override the default setting for an element with an element function.
### Exercises
1. Try out all the themes in ggthemes. Which do you like the best?
1. What aspects of the default theme do you like? What don't you like?
What would you change?
1. Look at the plots in your favourite scientific journal. What theme
do they most resemble? What are the main differences?
## Modifying theme components
To modify an individual theme component you use code like `plot + theme(element.name = element_function())`. In this section you'll learn about the basic element functions, and then in the next section, you'll see all the elements that you can modify. \indexf{theme}
There are four basic types of built-in element functions: text, lines, rectangles, and blank. Each element function has a set of parameters that control the appearance:
* `element_text()` draws labels and headings. You can control the font
`family`, `face`, `colour`, `size` (in points), `hjust`, `vjust`, `angle`
(in degrees) and `lineheight` (as ratio of `fontcase`). More details on
the parameters can be found in `vignette("ggplot2-specs")`.
Setting the font face is particularly challenging.
\index{Themes!labels} \indexf{element\_text}
```{r element_text}
base_t <- base + labs(title = "This is a ggplot") + xlab(NULL) + ylab(NULL)
base_t + theme(plot.title = element_text(size = 16))
base_t + theme(plot.title = element_text(face = "bold", colour = "red"))
base_t + theme(plot.title = element_text(hjust = 1))
```
You can control the margins around the text with the `margin` argument and
`margin()` function. `margin()` has four arguments: the amount of space
(in points) to add to the top, right, bottom and left sides of the text.
Any elements not specified default to 0.
```{r element_text-margin}
# The margins here look asymmetric because there are also plot margins
base_t + theme(plot.title = element_text(margin = margin()))
base_t + theme(plot.title = element_text(margin = margin(t = 10, b = 10)))
base_t + theme(axis.title.y = element_text(margin = margin(r = 10)))
```
* `element_line()` draws lines parameterised by `colour`, `size` and
`linetype`: \indexf{element\_line} \index{Themes!lines}
```{r element_line}
base + theme(panel.grid.major = element_line(colour = "black"))
base + theme(panel.grid.major = element_line(size = 2))
base + theme(panel.grid.major = element_line(linetype = "dotted"))
```
* `element_rect()` draws rectangles, mostly used for backgrounds, parameterised
by `fill` colour and border `colour`, `size` and `linetype`.
\index{Background} \index{Themes!background} \indexf{theme\_rect}
```{r element_rect}
base + theme(plot.background = element_rect(fill = "grey80", colour = NA))
base + theme(plot.background = element_rect(colour = "red", size = 2))
base + theme(panel.background = element_rect(fill = "linen"))
```
* `element_blank()` draws nothing. Use this if you don't want anything drawn,
and no space allocated for that element. The following example uses
`element_blank()` to progressively suppress the appearance of elements
we're not interested in. Notice how the plot automatically reclaims
the space previously used by these elements: if you don't want this to
happen (perhaps because they need to line up with other plots on the page),
use `colour = NA, fill = NA` to create invisible elements that
still take up space. \indexf{element\_blank}
```{r element_blank}
base
last_plot() + theme(panel.grid.minor = element_blank())
last_plot() + theme(panel.grid.major = element_blank())
```
```{r element_blank-2}
last_plot() + theme(panel.background = element_blank())
last_plot() + theme(
axis.title.x = element_blank(),
axis.title.y = element_blank()
)
last_plot() + theme(axis.line = element_line(colour = "grey50"))
```
* A few other settings take grid units. Create them with `unit(1, "cm")` or
`unit(0.25, "in")`.
To modify theme elements for all future plots, use `theme_update()`. It returns the previous theme settings, so you can easily restore the original parameters once you're done. \index{Themes!updating} \indexf{theme\_set}
`r columns(2, 3/4, 2/3)`
```{r theme-update}
old_theme <- theme_update(
plot.background = element_rect(fill = "lightblue3", colour = NA),
panel.background = element_rect(fill = "lightblue", colour = NA),
axis.text = element_text(colour = "linen"),
axis.title = element_text(colour = "linen")
)
base
theme_set(old_theme)
base
```
## Theme elements {#theme-elements}
There are around 40 unique elements that control the appearance of the plot. They can be roughly grouped into five categories: plot, axis, legend, panel and facet. The following sections describe each in turn. \index{Themes!elements}
### Plot elements
\index{Themes!plot}
Some elements affect the plot as a whole:
Element | Setter | Description
----------------------|------------------|------------
plot.background | `element_rect()` | plot background
plot.title | `element_text()` | plot title
plot.margin | `margin()` | margins around plot
`plot.background` draws a rectangle that underlies everything else on the plot. By default, ggplot2 uses a white background which ensures that the plot is usable wherever it might end up (e.g. even if you save as a png and put on a slide with a black background). When exporting plots to use in other systems, you might want to make the background transparent with `fill = NA`. Similarly, if you're embedding a plot in a system that already has margins you might want to eliminate the built-in margins. Note that a small margin is still necessary if you want to draw a border around the plot.
`r columns(3, 3/4)`
```{r plot}
base + theme(plot.background = element_rect(colour = "grey50", size = 2))
base + theme(
plot.background = element_rect(colour = "grey50", size = 2),
plot.margin = margin(2, 2, 2, 2)
)
base + theme(plot.background = element_rect(fill = "lightblue"))
```
### Axis elements {#theme-axis}
\index{Themes!axis} \index{Axis!styling}
The axis elements control the appearance of the axes:
Element | Setter | Description
--------------------|-------------------|---------------------------
axis.line | `element_line()` | line parallel to axis (hidden in default themes)
axis.text | `element_text()` | tick labels
axis.text.x | `element_text()` | x-axis tick labels
axis.text.y | `element_text()` | y-axis tick labels
axis.title | `element_text()` | axis titles
axis.title.x | `element_text()` | x-axis title
axis.title.y | `element_text()` | y-axis title
axis.ticks | `element_line()` | axis tick marks
axis.ticks.length | `unit()` | length of tick marks
Note that `axis.text` (and `axis.title`) comes in three forms: `axis.text`, `axis.text.x`, and `axis.text.y`. Use the first form if you want to modify the properties of both axes at once: any properties that you don't explicitly set in `axis.text.x` and `axis.text.y` will be inherited from `axis.text`.
```{r axis}
df <- data.frame(x = 1:3, y = 1:3)
base <- ggplot(df, aes(x, y)) + geom_point()
# Accentuate the axes
base + theme(axis.line = element_line(colour = "grey50", size = 1))
# Style both x and y axis labels
base + theme(axis.text = element_text(color = "blue", size = 12))
# Useful for long labels
base + theme(axis.text.x = element_text(angle = -90, vjust = 0.5))
```
The most common adjustment is to rotate the x-axis labels to avoid long overlapping labels. If you do this, note negative angles tend to look best and you should set `hjust = 0` and `vjust = 1`:
`r columns(2, 3/4)`
```{r axis-labels}
df <- data.frame(
x = c("label", "a long label", "an even longer label"),
y = 1:3
)
base <- ggplot(df, aes(x, y)) + geom_point()
base
base +
theme(axis.text.x = element_text(angle = -30, vjust = 1, hjust = 0)) +
xlab(NULL) +
ylab(NULL)
```
### Legend elements
\index{Themes!legend} \index{Legend!styling}
The legend elements control the apperance of all legends. You can also modify the appearance of individual legends by modifying the same elements in `guide_legend()` or `guide_colourbar()`.
Element | Setter | Description |
--------------------|---------------------------|---------------------------------------------|
legend.background | `element_rect()` | legend background |
legend.key | `element_rect()` | background of legend keys |
legend.key.size | `unit()` | legend key size |
legend.key.height | `unit()` | legend key height |
legend.key.width | `unit()` | legend key width |
legend.margin | `unit()` | legend margin |
legend.text | `element_text()` | legend labels |
legend.text.align | 0--1 | legend label alignment (0 = right, 1 = left)|
legend.title | `element_text()` | legend name |
legend.title.align | 0--1 | legend name alignment (0 = right, 1 = left) |
These options are illustrated below:
`r columns(3, 3/4)`
```{r legend}
df <- data.frame(x = 1:4, y = 1:4, z = rep(c("a", "b"), each = 2))
base <- ggplot(df, aes(x, y, colour = z)) + geom_point()
base + theme(
legend.background = element_rect(
fill = "lemonchiffon",
colour = "grey50",
size = 1
)
)
base + theme(
legend.key = element_rect(color = "grey50"),
legend.key.width = unit(0.9, "cm"),
legend.key.height = unit(0.75, "cm")
)
base + theme(
legend.text = element_text(size = 15),
legend.title = element_text(size = 15, face = "bold")
)
```
There are four other properties that control how legends are laid out in the context of the plot (`legend.position`, `legend.direction`, `legend.justification`, `legend.box`). They are described in Section \@ref(legend-layout).
### Panel elements
\index{Themes!panel} \index{Aspect ratio}
Panel elements control the appearance of the plotting panels:
Element | Setter | Description
--------------------|-------------------|-------------------------------
panel.background | `element_rect()` | panel background (under data)
panel.border | `element_rect()` | panel border (over data)
panel.grid.major | `element_line()` | major grid lines
panel.grid.major.x | `element_line()` | vertical major grid lines
panel.grid.major.y | `element_line()` | horizontal major grid lines
panel.grid.minor | `element_line()` | minor grid lines
panel.grid.minor.x | `element_line()` | vertical minor grid lines
panel.grid.minor.y | `element_line()` | horizontal minor grid lines
aspect.ratio | numeric | plot aspect ratio
The main difference between `panel.background` and `panel.border` is that the background is drawn underneath the data, and the border is drawn on top of it. For that reason, you'll always need to assign `fill = NA` when overriding `panel.border`.
```{r panel}
base <- ggplot(df, aes(x, y)) + geom_point()
# Modify background
base + theme(panel.background = element_rect(fill = "lightblue"))
# Tweak major grid lines
base + theme(
panel.grid.major = element_line(color = "gray60", size = 0.8)
)
# Just in one direction
base + theme(
panel.grid.major.x = element_line(color = "gray60", size = 0.8)
)
```
Note that aspect ratio controls the aspect ratio of the _panel_, not the overall plot:
```{r aspect-ratio}
base2 <- base + theme(plot.background = element_rect(colour = "grey50"))
# Wide screen
base2 + theme(aspect.ratio = 9 / 16)
# Long and skiny
base2 + theme(aspect.ratio = 2 / 1)
# Square
base2 + theme(aspect.ratio = 1)
```
### Faceting elements
\index{Themes!facets} \index{Faceting!styling}
The following theme elements are associated with faceted ggplots:
Element | Setter | Description |
--------------------|------------------|------------------------------------|
strip.background | `element_rect()` | background of panel strips |
strip.text | `element_text()` | strip text |
strip.text.x | `element_text()` | horizontal strip text |
strip.text.y | `element_text()` | vertical strip text |
panel.margin | `unit()` | margin between facets |
panel.margin.x | `unit()` | margin between facets (vertical) |
panel.margin.y | `unit()` | margin between facets (horizontal) |
Element `strip.text.x` affects both `facet_wrap()` or `facet_grid()`; `strip.text.y` only affects `facet_grid()`.
```{r faceting}
df <- data.frame(x = 1:4, y = 1:4, z = c("a", "a", "b", "b"))
base_f <- ggplot(df, aes(x, y)) + geom_point() + facet_wrap(~z)
base_f
base_f + theme(panel.margin = unit(0.5, "in"))
base_f + theme(
strip.background = element_rect(fill = "grey20", color = "grey80", size = 1),
strip.text = element_text(colour = "white")
)
```
### Exercises
1. Create the ugliest plot possible! (Contributed by Andrew D. Steen,
University of Tennessee - Knoxville)
1. `theme_dark()` makes the inside of the plot dark, but not the
outside. Change the plot background to black, and then update the
text settings so you can still read the labels.
1. Make an elegant theme that uses "linen" as the background colour and
a serif font for the text.
1. Systematically explore the effects of `hjust` when you have a multiline
title. Why doesn't `vjust` do anything?
## Saving your output {#saving}
When saving a plot to use in another program, you have two basic choices of output: raster or vector: \index{Exporting} \index{Saving output}
* Vector graphics describe a plot as sequence of operations: draw a line
from $(x_1, y_1)$ to $(x_2, y_2)$, draw a circle at $(x_3, x_4)$ with
radius $r$. This means that they are effectively 'infinitely' zoomable;
there is no loss of detail. The most useful vector graphic formats are
pdf and svg.
* Raster graphics are stored as an array of pixel colours and have a fixed
optimal viewing size. The most useful raster graphic format is png.
Figure \@ref(fig:vector-raster) illustrates the basic differences in these formats for a circle. A good description is available at <http://tinyurl.com/rstrvctr>.
```{r vector-raster, echo = FALSE, out.width = "100%", fig.cap="The schematic difference between raster (left) and vector (right) graphics."}
knitr::include_graphics("diagrams/vector-raster.png", dpi = 300, auto_pdf = TRUE)
```
Unless there is a compelling reason not to, use vector graphics: they look better in more places. There are two main reasons to use raster graphics:
* You have a plot (e.g. a scatterplot) with thousands of graphical objects
(i.e. points). A vector version will be large and slow to render.
* You want to embed the graphic in MS Office. MS has poor support for vector
graphics (except for their own DrawingXML format which is not currently
easy to make from R), so raster graphics are easier.
There are two ways to save output from ggplot2. You can use the standard R approach where you open a graphics device, generate the plot, then close the device: \indexf{pdf}
```R
pdf("output.pdf", width = 6, height = 6)
ggplot(mpg, aes(displ, cty)) + geom_point()
dev.off()
```
This works for all packages, but is verbose. ggplot2 provides a convenient shorthand with `ggsave()`:
```R
ggplot(mpg, aes(displ, cty)) + geom_point()
ggsave("output.pdf")
```
`ggsave()` is optimised for interactive use: you can use it after you've drawn a plot. It has the following important arguments: \indexf{ggsave}
* The first argument, `path`, specifies the path where the image should be
saved. The file extension will be used to automatically select the correct
graphics device. `ggsave()` can produce `.eps`, `.pdf`, `.svg`, `.wmf`,
`.png`, `.jpg`, `.bmp`, and `.tiff`.
* `width` and `height` control the output size, specified in inches. If left
blank, they'll use the size of the on-screen graphics device.
* For raster graphics (i.e. `.png`, `.jpg`), the `dpi` argument controls the
resolution of the plot. It defaults to 300, which is appropriate for most
printers, but you may want to use 600 for particularly high-resolution output,
or 96 for on-screen (e.g., web) display.
See `?ggsave` for more details.