r4ds/communicate-plots.Rmd

573 lines
27 KiB
Plaintext
Raw Normal View History

2016-08-17 03:22:50 +08:00
# Graphics for communication
2016-07-24 22:19:40 +08:00
## Introduction
2016-08-17 03:21:58 +08:00
In [exploratory data analysis], you learned how to use plots as tools for _exploration_. When making plots for exploration, you know---even before you look at them---which variables the plot would display. You made each plot for a purpose, could quickly look at it, and then move on to the next plot. In the course of most analyses you'll produce tens of hundreds of plots, most of which are immediately thrown in the trash.
2016-08-17 03:21:58 +08:00
Now you need to _communicate_ the result of your analysis to others. Your audience will not share your background knowledge and will not be deeply invested in the data. To help these newcomers quickly build up a good mental model of the data you will need to invest considerable effort to make your plots as self-explanatory as possible. In this chapter, you'll learn some of the tools that ggplot2 provides to do so.
2016-08-18 01:40:40 +08:00
The focus of this chapter is on the tools that you need to create good graphics. I assume you know what you want, and you just want to know how to do it. For that reason, I highly recommend pairing this advice with a good general visualisation book. I particularly like [_The Truthful Art_](https://amzn.com/0321934075), by Albert Cairo. I doesn't teach the mechanics of creating visualisations, but instead focusses on what you need to think about in order to create effective graphics.
2016-07-24 22:19:40 +08:00
### Prerequisites
In this chapter, we'll focus once again on ggplot2. We'll also use a little dplyr for data manipulation, and a few ggplot2 extension packages, including __ggrepel__ and __viridis__. Rather than loading those extensions here we'll refer to their functions explicitly with the `::` notation. That will help make it obvious what functions are built into ggplot2, and what functions come from other packages.
2016-07-24 22:19:40 +08:00
2016-08-17 06:06:51 +08:00
```{r, message = FALSE}
2016-07-24 22:19:40 +08:00
library(ggplot2)
library(dplyr)
2016-07-24 22:19:40 +08:00
```
2016-08-17 03:21:58 +08:00
## Label
2016-07-24 22:19:40 +08:00
2016-08-17 03:21:58 +08:00
The easiest place to start when turning an exploratory graphic into an expository graphic is with good labels. You can start with a plot title using `labs()`:
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(color = class)) +
geom_smooth(se = FALSE) +
labs(title = "Fuel efficiency decreases with engine size")
```
2016-08-17 03:21:58 +08:00
Generally, titles describe the main finding in the plot, not just what plot displays. If you need to add more text, there are two other useful labels that you can use in ggplot2 2.2.0 and above (which should be available by the time you're reading this book):
* `subtitle` adds additional detail in a smaller font beneath the title.
* `caption` adds text at the bottom right of the plot, often used to describe
the source of the data.
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(color = class)) +
geom_smooth(se = FALSE) +
labs(
title = "Fuel efficiency decreases with engine size",
2016-08-18 01:40:40 +08:00
subtitle = "Two seaters (sports cars) are an exception because of their light weight",
caption = "Data from fueleconomy.gov"
)
```
2016-08-17 03:21:58 +08:00
You can also use `labs()` to replace the axis and legend titles. It's usually a good idea to replace short variable names with more detailed descriptions, and to include the units.
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)) +
geom_smooth(se = FALSE) +
labs(
2016-08-18 01:40:40 +08:00
x = "Engine displacement (L)",
2016-08-17 03:21:58 +08:00
y = "Highway fuel economy (mpg)",
colour = "Car type"
2016-08-17 03:21:58 +08:00
)
2016-08-16 05:55:05 +08:00
```
2016-08-17 03:21:58 +08:00
It's possible to use mathematical equations instead of text strings. Just switch `""` out for `quote()` and read about the available options in `?plotmath`:
2016-08-16 05:55:05 +08:00
```{r}
2016-08-17 03:21:58 +08:00
df <- tibble(
x = runif(10),
y = runif(10)
)
ggplot(df, aes(x, y)) +
geom_point() +
labs(
x = quote(sum(x[i] ^ 2, i == 1, n)),
y = quote(alpha + beta + frac(delta, theta))
)
2016-08-16 05:55:05 +08:00
```
### Exercises
2016-08-18 01:40:40 +08:00
1. Create one plot of the fuel economy data with customized the `title`,
`subtitle`, `caption`, `x`, `y`, and `colour` labels.
1. The `geom_smooth()` is somewhat misleading because it the `hwy` for
large engines is skewed upwards because of the lightweight sports
cars with big engines. Use your modelling tools to fit and display
a better model.
2016-08-17 03:21:58 +08:00
1. Take an exploratory graphic that you've created in the last month, and add
informative titles to make it easier for others to understand.
2016-08-16 05:55:05 +08:00
## Annotations
2016-08-17 03:21:58 +08:00
As well as labelling major components of your plot, it's often useful to label individual observations or groups of observations. The first tool you have at your disposal is `geom_text()`. `geom_text()` is similar to `geom_point()`, but it has an additional aesthetic: `label`. This makes it possible to add textual labels to your plots.
2016-08-16 05:55:05 +08:00
2016-08-17 03:21:58 +08:00
There are two possible sources of labels. First, you might have a tibble that provides label. The plot below isn't terribly useful, but it illustrates a useful approach: pull out the most efficient car in each class with dplyr, and then label it on the plot:
2016-08-16 05:55:05 +08:00
```{r}
best_in_class <- mpg %>%
group_by(class) %>%
filter(row_number(desc(hwy)) == 1)
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)) +
geom_text(aes(label = model), data = best_in_class)
```
2016-08-17 03:21:58 +08:00
This is hard to read because the labels overlap with each other, and with the points. We can make things a little better by switching to `geom_label()` which draws a rectangle behind the text. We also use the `nudge_y` parameter to move the labels slightly above the corresponding points:
2016-08-16 05:55:05 +08:00
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)) +
geom_label(aes(label = model), data = best_in_class, nudge_y = 2, alpha = 0.5)
```
2016-08-17 03:21:58 +08:00
That helps a bit, but if you look closely in the top-left hand corner, you'll notice that there are two labels practically on top of each other. There's no way that we can fix these by applying the same transformation for every label. Instead, we can use the __ggrepel__ package by Kamil Slowikowski. This useful package will automatically adjust labels so that they don't overlap:
2016-08-16 05:55:05 +08:00
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)) +
2016-08-18 00:25:00 +08:00
geom_point(size = 3, shape = 1, data = best_in_class) +
2016-08-16 05:55:05 +08:00
ggrepel::geom_label_repel(aes(label = model), data = best_in_class)
```
2016-08-18 00:25:00 +08:00
Note the other handy technique here: I added a second layer of large, hollow points to highligh the points that I've labelled.
2016-08-17 03:21:58 +08:00
You can sometimes use the same idea to replace the legend with labels placed directly on the plot. It's not wonderful for this plot, but it isn't too bad. (`theme(legend.position = "none")` turns the legend off --- we'll talk about it more shortly).
2016-08-16 05:55:05 +08:00
```{r}
class_avg <- mpg %>%
group_by(class) %>%
summarise(
displ = median(displ),
hwy = median(hwy)
)
ggplot(mpg, aes(displ, hwy, colour = class)) +
ggrepel::geom_label_repel(aes(label = class),
data = class_avg,
size = 6,
label.size = 0,
segment.color = NA
) +
geom_point() +
theme(legend.position = "none")
```
2016-08-17 03:21:58 +08:00
Alternatively, you might just want to add a single label to the plot, but you'll still need to create a data frame. Often you want to the label in the corner of the plot, so it's convenient to create a new data frame using `summarise()`.
2016-08-16 05:55:05 +08:00
```{r}
label <- mpg %>%
summarise(
displ = max(displ),
hwy = max(hwy),
label = "Increasing engine size is \nrelated to decreasing fuel economy."
)
label
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_text(aes(label = label), data = label, vjust = "top", hjust = "right")
```
2016-08-17 03:21:58 +08:00
If you want to place the text exactly on the borders of the plot, you can use `+Inf` and `-Inf`. Since I'm no longer computing the positions from `mpg`, I use `tibble()` to create the data frame:
2016-08-16 05:55:05 +08:00
```{r}
label <- tibble(
2016-08-16 21:43:47 +08:00
displ = Inf,
hwy = Inf,
label = "Increasing engine size is \nrelated to decreasing fuel economy."
)
2016-08-16 05:55:05 +08:00
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_text(aes(label = label), data = label, vjust = "top", hjust = "right")
```
2016-08-17 03:21:58 +08:00
I manually broke the label up into lines using `"\n"`. Another approach is to use `stringr::str_wrap()` to automatically add linebreaks, given the number of characters you want per line:
2016-08-16 05:55:05 +08:00
```{r}
"Increasing engine size is related to decreasing fuel economy." %>%
stringr::str_wrap(width = 40) %>%
writeLines()
```
Also note the use of `hjust` and `vjust` to control the the alignment of the label. Figure \@ref(fig:just) shows all nine possible combinations.
2016-08-16 05:55:05 +08:00
```{r just, echo = FALSE, fig.cap = "All nine combinations of `hjust` and `vjust`."}
vjust <- c(bottom = 0, center = 0.5, top = 1)
hjust <- c(left = 0, center = 0.5, right = 1)
df <- tidyr::crossing(hj = names(hjust), vj = names(vjust)) %>%
mutate(
y = vjust[vj],
x = hjust[hj],
label = paste0("hjust = '", hj, "'\n", "vjust = '", vj, "'")
)
2016-08-17 03:21:58 +08:00
ggplot(df, aes(x, y)) +
geom_point(colour = "grey70", size = 5) +
geom_point(size = 0.5, colour = "red") +
2016-08-16 05:55:05 +08:00
geom_text(aes(label = label, hjust = hj, vjust = vj), size = 4)
```
2016-08-16 21:43:47 +08:00
Remember, as well as `geom_text()` you have all the other geoms in ggplot2 available to help annotate your plot. A few ideas:
* Use `geom_hline()` and `geom_vline()` to add reference lines. I often make
them thick (`size = 2`) and white (`colour = white`) and draw them
2016-08-17 03:21:58 +08:00
underneath the primary data layer. That makes them easy to see, but they
don't draw attention away from the data.
2016-08-16 21:43:47 +08:00
2016-08-17 03:21:58 +08:00
* Use `geom_rect()` to draw a rectangle around points of interest. The
2016-08-16 21:43:47 +08:00
boundaries of the rectangle are defined by aesthetics `xmin`, `xmax`,
`ymin`, `ymax`.
2016-08-17 03:21:58 +08:00
* Use `geom_segment()` with the `arrow` argument to draw attention
to a point with an arrow. Use aesthetics `x` and `y` to define the
2016-08-16 21:43:47 +08:00
starting location, and `xend` and `yend` to define the end location.
The only limitation is your imagination! (And your patience at position annotations in a way that looks good.)
2016-08-16 21:43:47 +08:00
2016-08-16 05:55:05 +08:00
### Exercises
2016-08-17 03:21:58 +08:00
1. Use `geom_text()` with infinite positions to place text at each corner
of the plot.
2016-08-16 21:43:47 +08:00
1. Read the documentation for `annotate()`. How can you use it to add a text
label to a plot without having to create a tibble?
2016-08-17 03:21:58 +08:00
1. How do labels with `geom_text()` interract with faceting? How can you
2016-08-17 03:21:58 +08:00
add a label to a single facet? How can you put a different label in
each facet? (Hint: think about the underlying data.)
2016-08-16 21:43:47 +08:00
1. What arguments to `geom_label()` control the appearance of the background
box?
2016-08-17 03:21:58 +08:00
1. What are the four argument to `arrow()`? How do they work? Create a series
of plot that demonstrate the most important options.
2016-07-24 22:19:40 +08:00
## Scales
2016-08-17 03:21:58 +08:00
The third way you can make your plot better for communication is to adjust the scales. Scales control the mapping from data values to things that you can perceive. Normally, ggplot2 automatically adds scales for you. When you type:
2016-07-24 22:19:40 +08:00
```{r default-scales, fig.show = "hide"}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class))
```
2016-08-17 03:21:58 +08:00
Behind the scenes, ggplot2 automatically adds default scales:
```{r, fig.show = "hide"}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)) +
scale_x_continuous() +
scale_y_continuous() +
scale_colour_discrete()
```
2016-08-17 03:21:58 +08:00
Note the naming scheme for scales: `scale_` followed by the name of the aesthetic, then `_`, then the name of the scale. The default scales are named according to the type of variable they with: continuous, discrete, datetime, or date. There are lots of non-default scales which you'll learn about below.
The default scales have been carefully chosen to do a good job for a wide range of inputs. But you might want to override the defaults for two reasons:
2016-08-16 05:55:05 +08:00
* You might want to tweak some of the parameters of the default scale.
2016-08-17 03:21:58 +08:00
This allows you to do things like change the breaks on the axes, or the
key labels on the legend.
2016-08-16 05:55:05 +08:00
2016-08-17 03:21:58 +08:00
* You might want to replace the scale altogether, and use a completely
different algorithm. Often you can beat the default because you know
more about the data.
2016-08-16 21:43:47 +08:00
### Axis ticks and legend keys
2016-08-17 03:21:58 +08:00
There are two primary arguments that affect the appearance of the ticks on the axes and the keys on the legend: `breaks` and `labels`. Breaks controls the position of the ticks, or the values associated with the keys. Labels controls the text label associated with each tick/key. The most common use of `breaks` is to override the defaults choice:
2016-08-16 21:43:47 +08:00
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
scale_y_continuous(breaks = seq(15, 40, by = 5))
```
2016-08-17 03:21:58 +08:00
You can use `labels` in the same way (a character vector the same length as `breaks`), but you can also set it to `NULL` to suppress the labels altogether. This is useful for maps, or for publishing plots where you can't share the absolute numbers.
2016-08-16 21:43:47 +08:00
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
scale_x_continuous(labels = NULL) +
scale_y_continuous(labels = NULL)
```
2016-08-17 03:21:58 +08:00
You can also use `breaks` and `labels` control the apperance of legends. Collecting axes and legends are called guides. Axes are used for x and y aesthetics; legends are used used for everything else.
Another use of `breaks` is when you have relatively few data points and want to highlight exactly where the observations occur. For example, take this plot that shows when each US president started and ended their term.
```{r}
presidential %>%
mutate(id = 33 + row_number()) %>%
ggplot(aes(start, id)) +
geom_point() +
geom_segment(aes(xend = end, yend = id)) +
scale_x_date(NULL, breaks = presidential$start, date_labels = "'%y")
```
2016-08-17 03:21:58 +08:00
Note that the specification of breaks and labels for date and datetime scales is a little different:
2016-08-16 21:43:47 +08:00
* `date_labels` takes a format specification, in the same form as
`parse_datetime()`.
* `date_breaks` (not shown here), takes a string like "2 days" or "1 month".
2016-08-17 03:21:58 +08:00
### Legend layout
You most often use `breaks` and `labels` to tweak the axes. While they both also work for legends, there are a few other techniques your more likely to use.
To control the overall position of the legend, you need to use a `theme()` setting. We'll come back to themes at the end of the chapter, but in brief, they control the non-data parts of the plot. The themes setting `legend.position` controls where the legend is drawn:
```{r fig.asp = 1, fig.align = "default", out.width = "50%", fig.width = 3}
base <- ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class))
2016-08-18 02:52:12 +08:00
base + theme(legend.position = "left")
2016-08-17 03:21:58 +08:00
base + theme(legend.position = "top")
base + theme(legend.position = "bottom")
2016-08-18 02:52:12 +08:00
base + theme(legend.position = "right") # the default
2016-08-17 03:21:58 +08:00
```
You can also use `legend.postion = "none"` to suppress the display of the legend altogether.
To control the display of individual legneds, use `guides()` along with `guide_legend()` or `guide_colourbar()`. The following example shows two important settings: controlling the number of rows with `nrow`, and overriding one of the aesthetics to make the points bigger. This is particularly useful if you have used a low `alpha` to display many points on a plot.
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)) +
geom_smooth(se = FALSE) +
theme(legend.position = "bottom") +
guides(colour = guide_legend(nrow = 1, override.aes = list(size = 4)))
```
### Replacing a scale
Instead of just tweaking the detail a little, you can also replace the scale altogether. We'll focus on colour scales because there are many options, and they're the scales you're mostly likely to want to change. The same principles apply to the other aesthetics. All colour scales have two variants: `scale_colour_x()` and `scale_fill_x()` for the `colour` and `fill` aesthetics respectively (And the colour scales are available in both UK and US spellings.)
The default categorical scale picks colours that are evenly spaced around the colour wheel. Useful alternatives are the ColourBrewer scales which have been hand tuned to work better for people with common types of colour blindness. The two plots below don't look that different, but there's enough difference in the shades of red and green that they can be distinguished even by people with red-green colour blindness.
2016-08-16 21:43:47 +08:00
```{r, fig.align = "default", out.width = "50%"}
ggplot(mpg, aes(displ, hwy)) +
2016-08-16 21:43:47 +08:00
geom_point(aes(color = drv))
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(color = drv)) +
scale_colour_brewer(palette = "Set1")
```
2016-08-18 01:40:40 +08:00
Don't forget simpler techniques. If there are just a few colours, you can add a redundant shape mapping. This will also ensure your plot works well in black and white.
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(color = drv, shape = drv)) +
scale_colour_brewer(palette = "Set1")
```
Figure \@ref(fig:brewer) shows the complete list of all palettes. The sequential (top) and diverging (bottom) palettes are particularly useful if your categorical values are ordered, or have a "middle". This often arises if you've used `cut()` to make a continuous varible into a categorical variable.
```{r brewer, fig.asp = 2.5, echo = FALSE, fig.cap = "All ColourBrewer scales."}
par(mar = c(0, 3, 0, 0))
RColorBrewer::display.brewer.all()
```
When you have a predefined mapping between values and colours use `scale_colour_manual()`. For example, if we map presidential party to colour, we want to use the standard mapping of red for Republicans and blue for Democrats:
```{r}
presidential %>%
mutate(id = 33 + row_number()) %>%
ggplot(aes(start, id, colour = party)) +
geom_point() +
geom_segment(aes(xend = end, yend = id)) +
scale_colour_manual(values = c(Republican = "Red", Democratic = "Blue"))
```
For continuous colour, you can use the built-in `scale_colour_gradient()` or `scale_fill_gradient()`. If you have a diverging scale, you can use `scale_colour_gradient2()`. That allows you to give, for example, positive and negative values different colours. That's sometimes also useful if you want to distinguish points above or below the mean.
2016-08-16 21:43:47 +08:00
Another option is `scale_colour_viridis()` provided by the __viridis__ package. It's a continuous analog of the categorical Brewer scales. The designers, Nathaniel Smith and Stéfan van der Walt, carefully tailored a continuous colour scheme that has good perceptual properities. Here's an example from the viridis vignette.
```{r, fig.align = "default", fig.asp = 1, out.width = "50%", fig.width = 4}
df <- tibble(
x = rnorm(10000),
y = rnorm(10000)
)
ggplot(df, aes(x, y)) +
geom_hex() +
coord_fixed()
ggplot(df, aes(x, y)) +
geom_hex() +
viridis::scale_fill_viridis() +
coord_fixed()
```
### Exercises
2016-08-17 03:21:58 +08:00
1. Why doesn't the following code override the default scale?
```{r fig.show = "hide"}
ggplot(df, aes(x, y)) +
geom_hex() +
scale_colour_gradient(low = "white", high = "red") +
coord_fixed()
```
2016-08-17 03:21:58 +08:00
1. What is first argument to every scale? How does it compare to to `labs()`?
2016-08-16 21:43:47 +08:00
2016-08-17 03:21:58 +08:00
1. Change the display of the presidential terms by:
2016-08-16 21:43:47 +08:00
2016-08-17 03:21:58 +08:00
1. Combining the two variants shown above.
1. Improve the display of the y axis.
1. Labelling each term with the name of the president.
2016-08-17 03:21:58 +08:00
1. Adding informative plot labels.
1. Placing breaks every 4 years (this is trickier than it seems!).
1. Use `override.aes` to make the legend on the following plot easier to see.
2016-08-17 03:21:58 +08:00
```{r, dev = "png"}
ggplot(diamonds, aes(carat, price)) +
geom_point(aes(colour = cut), alpha = 1/20)
```
2016-07-24 22:19:40 +08:00
## Zooming
2016-08-17 03:21:58 +08:00
There are three ways to control the plot limits:
2016-08-16 21:43:47 +08:00
2016-08-17 03:21:58 +08:00
1. By controlling the data.
1. Setting the limits in each scale.
1. Setting `xlim` and `ylim` in `coord_cartesian()`.
2016-08-16 21:43:47 +08:00
2016-08-17 03:21:58 +08:00
To zoom in on a region of the plot, it's generally best to use `coord_cartesian()`. Compare the following two plots:
```{r out.width = "50%", fig.align = "default"}
ggplot(mpg, mapping = aes(displ, hwy)) +
geom_point(aes(color = class)) +
geom_smooth() +
coord_cartesian(xlim = c(5, 7), ylim = c(10, 30))
mpg %>%
filter(displ >= 5, displ <= 7, hwy >= 10, hwy <= 30) %>%
ggplot(aes(displ, hwy)) +
geom_point(aes(color = class)) +
geom_smooth() +
coord_cartesian(xlim = c(5, 7), ylim = c(10, 30))
```
You can also set the `limits` on individual scales. If you are reducing the limits, this is basically equivalent to subsetting the data. It's more useful if you want _expand_ the limits, for example for matching scales across different plots. Take the following toy example: if we extract out two classes of cars and plot them separately, it's hard to compare the plots because all three scales have different ranges.
2016-08-16 21:43:47 +08:00
```{r out.width = "50%", fig.align = "default", fig.width = 4}
suv <- mpg %>% filter(class == "suv")
compact <- mpg %>% filter(class == "compact")
2016-08-16 21:43:47 +08:00
ggplot(suv, aes(displ, hwy, colour = drv)) +
geom_point()
ggplot(compact, aes(displ, hwy, colour = drv)) +
geom_point()
```
One way to overcome this problem is to share scales across multiple plots, training the scales with the `limits` of the full data.
```{r out.width = "50%", fig.align = "default", fig.width = 4}
x_scale <- scale_x_continuous(limits = range(mpg$displ))
y_scale <- scale_y_continuous(limits = range(mpg$hwy))
col_scale <- scale_colour_discrete(limits = unique(mpg$drv))
2016-08-16 21:43:47 +08:00
ggplot(suv, aes(displ, hwy, colour = drv)) +
geom_point() +
x_scale +
y_scale +
col_scale
ggplot(compact, aes(displ, hwy, colour = drv)) +
geom_point() +
x_scale +
y_scale +
col_scale
```
In this case you could have used faceting, but this technique is broadly useful if you want to make your plots are comparable even when spread across multiple pages of your final report.
2016-08-16 21:43:47 +08:00
## Themes
2016-08-17 03:21:58 +08:00
Finally, you can customize the non-data elements of your plot with a theme:
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(color = class)) +
geom_smooth(se = FALSE) +
theme_bw()
```
2016-08-17 06:06:51 +08:00
ggplot2 includes eight themes by default, as shown in Figure \@ref(fig:themes). Many more are included in add-on packages like __ggthemes__ (<https://github.com/jrnold/ggthemes>), by Jeffrey Arnold.
2016-08-16 21:43:47 +08:00
```{r themes, echo = FALSE, fig.cap = "The eight themes built-in to ggplot2."}
knitr::include_graphics("images/visualization-themes.png")
```
Many people wonder why the default theme has a grey background. This was a deliberate choice because it puts the data forward while still making the grid lines visible. The white grid lines are visible (which is important because they significantly aid position judgements), 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.
2016-08-17 03:21:58 +08:00
It's also possible to control individual components of each theme, like the size and colour of the font used for the y axis. This unfortunately is outside the scope of this book, so you'll need to read the ggplot2 book for the full details. You can also create your own themes if you have a corporate style or you're trying to match a journal style.
2016-08-16 05:55:05 +08:00
2016-08-18 00:25:13 +08:00
## Saving your plots
2016-08-18 05:24:10 +08:00
There are two main ways to get your plots out of R and into your final write-up: `ggsave()` and knitr. `ggsave()` will save the most recent plot to disk:
2016-08-18 00:25:13 +08:00
```{r, fig.show = "none"}
ggplot(mpg, aes(displ, hwy)) + geom_point()
ggsave("my-plot.pdf")
```
```{r, include = FALSE}
file.remove("my-plot.pdf")
```
If you don't specify the `width` and `height` they will be taken from dimensions of the current plotting device. For reproducible code, you'll want to specify them.
2016-08-18 05:07:51 +08:00
Generally, however, I think you should be assembling your final reports using knitr and rmarkdown, so I want to focus on the important chunk options that you should know about for graphics. You can learn more about `ggsave()` in the documentation.
2016-08-18 00:25:13 +08:00
### Figure sizing
2016-08-18 05:07:51 +08:00
The biggest challenge of graphics in RMarkdown is getting your figures the right size and shape. There are five main options that control figure sizing: `fig.width`, `fig.height`, `fig.asp`, `out.width` and `out.height`. Image sizing is challenging because there are two sizes (the size of the figure created by R and the size in which it is inserted in the output document), and multiple ways of specifying the size (height, width, aspect ratio: pick two out of three).
2016-08-18 00:25:13 +08:00
2016-08-18 05:07:51 +08:00
I only ever use three of the five options:
* I find it most aesthetically pleasing for plots to have a consistent
width. To enforce this I set `fig.width = 6` (6") and `fig.asp = 0.618`
(the golden ratio) in the defaults. Then in individual chunks, I only
adjust `fig.asp`.
2016-08-18 00:25:13 +08:00
* I control the output size with `out.width` and set it to a percentage
2016-08-18 05:07:51 +08:00
of the line width). I default to `out.width = "70%"`
and `fig.align = 'center'`. That give plots room to breath, without taking
up too much space.
2016-08-18 00:25:13 +08:00
2016-08-18 05:07:51 +08:00
* To put multiple plots in a single row I set the `out.width` to
`50%` for two plots, `33%` for 3 plots, or `25%` to 4 plots, and set
fig.align = "default"`. Depending on what I'm trying to illustrate (e.g.
show data or show plot variations), I'll also tweak `fig.width`, as
discussed below.
If you find that you're having to squint to read the text in your plot, you need to tweak `fig.width`. If `fig.width` is larger than the size the figure is rendered in the final doc, the text will be too small; if `fig.width` is smaller, the text will be too big. You'll often need to do a little experimentation to figure out the right ratio between the `fig.width` and the eventual width in your document. To illustrate the principle, the following three plots have `fig.width` of 4, 6, and 8 respectively:
2016-08-18 00:25:13 +08:00
```{r, include = FALSE}
plot <- ggplot(mpg, aes(displ, hwy)) + geom_point()
```
```{r, fig.width = 4, echo = FALSE}
plot
```
```{r, fig.width = 6, echo = FALSE}
plot
```
```{r, fig.width = 8, echo = FALSE}
plot
```
2016-08-18 05:07:51 +08:00
If you want to make sure the font size is the same in all your figures, whenever you set `out.width`, you'll also need to adjust `fig.width` to maintain the same ratio with your default `out.width`. For example, if your default `fig.width` is 6 and `out.width` is 0.7, when you set `out.width = "50%"` you'll need to set `fig.width` to 4.2 (6 * 0.5 / 0.7).
### Other important options
2016-08-18 00:25:13 +08:00
2016-08-18 05:07:51 +08:00
When mingling code and text, like I do in this book, I recommend setting `fig.show = "hold"` so that that plots are shown after the code. This has the pleasant side effect of forcing you to break up large blocks of code with their explanations.
2016-08-18 00:25:13 +08:00
2016-08-18 05:07:51 +08:00
To add a caption to the plot, use `fig.cap`. In RMarkdown this will change the figure from inline to "floating".
2016-08-18 00:25:13 +08:00
2016-08-18 05:07:51 +08:00
If you're producing pdf output, the default graphics type is PDF. This a good default because PDFs are high quality vector graphics. However, they can produce very large and slow plots if you are displaying thousands of points. In that case, set `dev = "png"` to force the use of PNGs. They are slightly lower quality, but will be much more compact.
2016-08-18 00:25:13 +08:00
2016-08-16 05:55:05 +08:00
## Learning more
2016-08-17 03:21:58 +08:00
The absolute best place to learn more is the ggplot2 book: [_ggplot2: Elegant graphics for data analysis_](https://amzn.com/331924275X). It goes into much more depth about the underlying theory, and has many more examples of how to combine the individual pieces to solve practical problems. Unfortunately the book is not available online for free, although can find the source code at <https://github.com/hadley/ggplot2-book>.
2016-08-16 05:55:05 +08:00
Another great resource is the ggplot2 extensions guide at <http://www.ggplot2-exts.org/>. This lists many of the packages that extend ggplot2 with new geoms and scales. It's a great place to start if you're trying to do something that seems really hard with ggplot2.