Tweaks to visualize

This commit is contained in:
hadley 2016-07-21 16:54:19 -05:00
parent 48351807ba
commit 6b6c867c51
2 changed files with 227 additions and 210 deletions

View File

@ -442,7 +442,7 @@ ggplot(data = diamonds) +
geom_point(aes(x = carat, y = price))
```
Scatterplots become less useful as the size of your dataset grows, because points begin to pile up into areas of uniform black (as above). This problem is known as __overplotting__. This problem is similar to showing the distribution of price by color using a scatterplot:
Scatterplots become less useful as the size of your dataset grows, because points begin to overplot, and pile up into areas of uniform black (as above). This problem is similar to showing the distribution of price by color using a scatterplot:
```{r, dev = "png"}
ggplot(data = diamonds, mapping = aes(x = price, y = cut)) +

View File

@ -1,11 +1,13 @@
# Data visualisation
## Introduction
> "The simple graph has brought more information to the data analysts mind
> than any other device." --- John Tukey
This chapter will teach you how to visualize your data with R and ggplot2. R contains several systems for making graphs, but the ggplot2 system is one of the most beautiful and most versatile. ggplot2 implements the __grammar of graphics__, a coherent system for describing and building graphs. With ggplot2, you can do more faster by learning one system and applying it in many places.
This chapter will teach you how to visualize your data ggplot2. R has several systems for making graphs, but ggplot2 is one of the most elegant and most versatile. ggplot2 implements the __grammar of graphics__, a coherent system for describing and building graphs. With ggplot2, you can do more faster by learning one system and applying it in many places.
## Prerequisites
### Prerequisites
To access the datasets, help pages, and functions that we will use in this chapter, load ggplot2 using the `library()` function. We'll also load tibble, which you'll learn about later. It improves the default printing of datasets.
@ -37,13 +39,13 @@ mpg
The dataset contains observations collected by the EPA on 38 models of car. Among the variables in `mpg` are:
1. `displ` - a car's engine size in litres, and
1. `displ`, a car's engine size, in litres.
1. `hwy` - a car's fuel efficiency on the highway in miles per gallon (mpg).
1. `hwy`, a car's fuel efficiency on the highway, in miles per gallon (mpg).
A car with a low fuel efficiency consumes more fuel than a car with a high
fuel efficiency when they travel the same distance.
To learn more about `mpg`, open its help page with the command `?mpg`.
To learn more about `mpg`, open its help page by running `?mpg`.
To plot `mpg`, open an R session and run the code below. The code plots the `mpg` data by putting `displ` on the x-axis and `hwy` on the y-axis:
@ -61,15 +63,11 @@ ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))
```
With ggplot2, you begin a plot with the function `ggplot()`. `ggplot()` creates a coordinate system that you can add layers to. The first argument of `ggplot()` is the dataset to use in the graph. So `ggplot(data = mpg)` creates an empty graph that will use the `mpg` dataset:
With ggplot2, you begin a plot with the function `ggplot()`. `ggplot()` creates a coordinate system that you can add layers to. The first argument of `ggplot()` is the dataset to use in the graph. So `ggplot(data = mpg)` creates an empty graph, but it's not very interesting so I'm not going to show it here.
```{r}
ggplot(data = mpg)
```
You complete your graph by adding one or more layers to `ggplot()`. The function `geom_point()` adds a layer of points to your plot, which creates a scatterplot. ggplot2 comes with many geom functions that each add a different type of layer to a plot. You'll learn a whole bunch of them through out this chapter.
You complete your graph by adding one or more layers to `ggplot()`. Here, the function `geom_point()` adds a layer of points to your plot, which creates a scatterplot. ggplot2 comes with many geom functions that each add a different type of layer to a plot.
Each geom function in ggplot2 takes a mapping argument. The mapping argument of your geom function explains where your points should go. You must set `mapping` to a call to `aes()`. The `x` and `y` arguments of `aes()` explain which variables to map to the x and y axes of your plot. `ggplot()` will look for those variables in your dataset, `mpg`.
Each geom function in ggplot2 takes a `mapping` argument. This defines how variables in your dataset and mapped to visual properties. You must always use `mapping()` in conjunction with `aes()`. The `x` and `y` arguments of `aes()` describe which variables to map to the x and y axes of your plot, and ggplot2 will look for those variables in your dataset, `mpg`.
Let's turn this code into a reusable template for making graphs with ggplot2. To make a graph, replace the bracketed sections in the code below with a dataset, a geom function, or a set of mappings.
@ -80,6 +78,18 @@ ggplot(data = <DATA>) +
The rest of this chapter will show you how to complete and extend this template to make different types of graphs. We will begin with the `<MAPPINGS>` component.
### Exercises
1. Run `ggplot(data = mpg)` what do you see?
1. What does the `drv` variable describe? Read the help for `?mpg` to find
out.
1. Make a scatterplot of `hwy` vs `cyl`.
1. What happens if you make a scatterplot of `class` vs `drv`. Why is
the plot not useful?
## Aesthetic mappings
> "The greatest value of a picture is when it forces us to notice what we
@ -95,9 +105,7 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
Let's hypothesize that the cars are hybrids. One way to test this hypothesis is to look at the `class` value for each car. The `class` variable of the `mpg` dataset classifies cars into groups such as compact, midsize, and suv. If the outlying points are hybrids, they should be classified as compact cars or, perhaps, subcompact cars (keep in mind that this data was collected before hybrid trucks and suvs became popular).
You can add a third variable, like `class`, to a two dimensional scatterplot by mapping it to an _aesthetic_.
An aesthetic is a visual property of the objects in your plot. Aesthetics include things like the size, the shape, or the color of your points. You can display a point (like the one below) in different ways by changing the values of its aesthetic properties. Since we already use the word "value" to describe data, let's use the word "level" to describe aesthetic properties. Here we change the levels of a point's size, shape, and color to make the point small, triangular, or blue.
You can add a third variable, like `class`, to a two dimensional scatterplot by mapping it to an __aesthetic__. An aesthetic is a visual property of the objects in your plot. Aesthetics include things like the size, the shape, or the color of your points. You can display a point (like the one below) in different ways by changing the values of its aesthetic properties. Since we already use the word "value" to describe data, let's use the word "level" to describe aesthetic properties. Here we change the levels of a point's size, shape, and color to make the point small, triangular, or blue:
```{r, echo = FALSE, asp = 1/4}
ggplot() +
@ -107,7 +115,7 @@ ggplot() +
geom_point(aes(4, 1), size = 20, colour = "blue") +
scale_x_continuous(NULL, limits = c(0.5, 4.5), labels = NULL) +
scale_y_continuous(NULL, limits = c(0.9, 1.1), labels = NULL) +
theme(aspect.ratio = 1/4)
theme(aspect.ratio = 1/3)
```
You can convey information about your data by mapping the aesthetics in your plot to the variables in your dataset. For example, you can map the colors of your points to the `class` variable to reveal the class of each car.
@ -119,57 +127,53 @@ ggplot(data = mpg) +
(If you prefer British English, like Hadley, you can use `colour` instead of `color`.)
To map an aesthetic to a variable, set the name of the aesthetic to the name of the variable, _and do this in your plot's `aes()` call_. ggplot2 will automatically assign a unique level of the aesthetic (here a unique color) to each unique value of the variable, a process known as _mapping_. ggplot2 will also add a legend that explains which levels correspond to which values.
To map an aesthetic to a variable, set the name of the aesthetic to the name of the variable inside `aes()`. ggplot2 will automatically assign a unique level of the aesthetic (here a unique color) to each unique value of the variable, a process known as __scaling__. ggplot2 will also add a legend that explains which levels correspond to which values.
The colors reveal that many of the unusual points are two seater cars. These cars don't seem like hybrids. In fact, they seem like sports cars---and that's what they are. Sports cars have large engines like suvs and pickup trucks, but small bodies like midsize and compact cars, which improves their gas mileage. In hindsight, these cars were unlikely to be hybrids since they have large engines.
The colors reveal that many of the unusual points are two seater cars. These cars aren't seem like hybrids, and are, in fact, sports cars! Sports cars have large engines like SUVs and pickup trucks, but small bodies like midsize and compact cars, which improves their gas mileage. In hindsight, these cars were unlikely to be hybrids since they have large engines.
In the above example, we mapped `class` to the color aesthetic, but we could have mapped `class` to the size aesthetic in the same way. In this case, the exact size of each point would reveal its class affiliation.
In the above example, we mapped `class` to the color aesthetic, but we could have mapped `class` to the size aesthetic in the same way. In this case, the exact size of each point would reveal its class affiliation. We get a _warning_ here, because mapping an unordered variable (`class`) to an ordered aesthetic (`size`) is not a good idea.
```{r}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, size = class))
```
Or we could have mapped `class` to the _alpha_ aesthetic, which controls the transparency of the points. Now the transparency of each point corresponds to its class affiliation.
Or we could have mapped `class` to the _alpha_ aesthetic, which controls the transparency of the points, or the shape of the points.
```{r}
```{r out.width = "50%", fig.align = 'default', warning = FALSE, fig.asp = 1/2}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, alpha = class))
```
We also could have mapped `class` to the shape of the points.
```{r warning=FALSE}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, shape = class))
```
What happened to the suvs? ggplot2 will only use six shapes at a time. Additional groups will go unplotted when you use this aesthetic.
What happened to the SUVs? ggplot2 will only use six shapes at a time. Additional groups will go unplotted when you use this aesthetic.
For each aesthetic, you set the name of the aesthetic to the variable to display, and you do this within the `aes()` function. The `aes()` function gathers together each of the aesthetic mappings used by a layer and passes them to the layer's mapping argument. The syntax highlights a useful insight about `x` and `y`: the x and y locations of a point are themselves aesthetics, visual properties that you can map to variables to display information about the data.
For each aesthetic, you set the name of the aesthetic to the variable to display within the `aes()` function. The `aes()` function gathers together each of the aesthetic mappings used by a layer and passes them to the layer's mapping argument. The syntax highlights a useful insight about `x` and `y`: the x and y locations of a point are themselves aesthetics, visual properties that you can map to variables to display information about the data.
Once you set an aesthetic, ggplot2 takes care of the rest. It selects a pleasing set of levels to use for the aesthetic, and it constructs a legend that explains the mapping between levels and values. For x and y aesthetics, ggplot2 does not create a legend, but it creates an axis line with tick marks and a label. The axis line acts as a legend; it explains the mapping between locations and values.
Once you set an aesthetic, ggplot2 takes care of the rest. It selects a reasonable scale to use with the aesthetic, and it constructs a legend that explains the mapping between levels and values. For x and y aesthetics, ggplot2 does not create a legend, but it creates an axis line with tick marks and a label. The axis line acts as a legend; it explains the mapping between locations and values.
You can also set the aesthetic properties of your geom manually. For example, we can make all of the points in our plot blue.
You can also set the aesthetic properties of your geom manually. For example, we can make all of the points in our plot blue:
```{r}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy), color = "blue")
```
Here, the color doesn't convey information about a variable. It only changes the appearance of the plot. To set an aesthetic manually, do not place it in the `aes()` function. Call the aesthetic by name as an argument of your geom function. Then pass the aesthetic a level that R will recognize, such as
Here, the color doesn't convey information about a variable, but only changes the appearance of the plot. To set an aesthetic manually, set the aesthetic by name as an argument of your geom function. You'll need to pick a value that makes sense for that aesthetic:
* the name of a color as a character string.
* the size of a point in mm.
* the shape as a point as a number, as shown below.
R uses the following numeric codes to refer to the following shapes.
R has a set of 24 built-in shapes, identified by numbers:
```{r echo = FALSE}
```{r echo = FALSE, out.width = "75%", fig.asp = 1/3}
shapes <- tibble(
shape = c(0:19, 22, 21, 24, 23, 20),
x = 0:24 %/% 5,
y = -(0:24 %% 5)
x = (0:24 %/% 5) / 2,
y = (-(0:24 %% 5)) / 4
)
ggplot(shapes, aes(x, y)) +
geom_point(aes(shape = shape), size = 5, fill = "red") +
@ -177,55 +181,65 @@ ggplot(shapes, aes(x, y)) +
scale_shape_identity() +
expand_limits(x = 4.1) +
scale_x_continuous(NULL, breaks = NULL) +
scale_y_continuous(NULL, breaks = NULL)
scale_y_continuous(NULL, breaks = NULL, limits = c(-1.2, 0.2)) +
theme_minimal() +
theme(aspect.ratio = 1/2.75)
```
If you get an odd result, double check that you are calling the aesthetic as its own argument (and not calling it from inside of `mapping = aes()`). I like to think of aesthetics like this, if you place the aesthetic:
* _inside_ of the `aes()` function, ggplot2 will **map** the aesthetic to data
values and build a legend.
* _outside_ of the `aes()` function, ggplot2 will **set** the aesthetic to a
level that you supply manually.
### Exercises
1. Which variables in the `mpg` are discrete? Which variables are continuous?
(Hint: type `?mpg` to read the documentation for the dataset). How
1. Map a discrete variable to `color`, `size`, and `shape`. Then map a
continuous variable to each aesthetic. How does ggplot2 behave differently for
discrete vs. continuous variables?
1. What happens if you map multiple variables to the same aesthetic?
What happens when you map multiple variables to different aesthetics?
1. What other aesthetics can `geom_point()` take? (Hint: use `?geom_point`)
1. What happens if you set an aesthetic to something other than a variable
name, like `displ < 5`?
1. What happens if you accidentally write this code? Why are the points
not blue?
1. What's gone wrong with this code? Why are the points not blue?
```{r}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, color = "blue"))
```
1. Which variables in `mpg` are discrete? Which variables are continuous?
(Hint: type `?mpg` to read the documentation for the dataset). How
can you see this information when you run `mpg`?
1. Map a continuous variable to `color`, `size`, and `shape`. How do
these aesthetics behave differently for discrete vs. continuous variables?
1. What happens if you map the same variable across multiple aesthetics?
What happens if you map different variables across multiple aesthetics?
1. What does the `stroke` aesthetic do? What shapes does it work with?
(Hint: use `?geom_point`)
1. What happens if you set an aesthetic to something other than a variable
name, like `displ < 5`?
1. Vignettes are long-form guides the documentation things about
a package that affect many functions. ggplot2 has two vignettes
what do they describe?
a package that affect many functions. ggplot2 has two vignettes.
How can you find them and what do they describe? (Hint: google is
your friend.)
## Common problems
As you start to run R code, you're likely to run into problems. Don't worry --- it happens to everyone. I have been writing R code for years, and every day I still write code that doesn't work!
Start by carefully comparing the code that you're running to the code in the book. R is extremely picky, and a misplaced character can make all the difference. Make sure that every `(` is matched with a `)` and every `"` is paired with another `"`. Sometimes you'll run the code and nothing happens. Check the left-hand of your console: if it's a `+`, it means that R doesn't think you've typed a complete expression and it's waiting for you to finish it. In this case, it's usually easiest to start from scratch again by pressing `Escape` to abort processing the current command.
One common problem when creating ggplot2 graphics is to put the `+` in the wrong place: it has to come at the end of the line, not the start. In other words, make sure you haven't accidentally written code this:
```R
ggplot(data = mpg)
+ geom_point(mapping = aes(x = displ, y = hwy))
```
If you're still stuck, try the help. You can get help about any R function or by runnning `?function_name` in the console, or selecting the function name and pressing F1 in RStudio. Don't worry if the help doesn't seem that helpful - instead skip down to the examples and look for code that matches what you're trying to do.
If that doesn't help, carefully read the error message. Sometimes the answer will be buried there! But when you're new to R, the answer might be but you don't yet know how to understand it. Another great tool is google: trying googling the error message, as it's likely someone else has had the same problem, and have gotten help on line.
## Facets
One way to add additional variables is with aesthetics. Another way, particularly useful for categorical variables is to split your plot into _facets_, subplots that each display a subset of the data.
One way to add additional variables is with aesthetics. Another way, particularly useful for categorical variables, is to split your plot into __facets__, subplots that each display one subset of the data.
To facet your plot, add `facet_wrap()` to your plot call. The first argument of `facet_wrap()` is a formula, which you create with `~` followed by a variable name (here "formula" is the name of a data structure in R, not a synonym for "equation"). The variable that you pass to `facet_wrap()` should be discrete.
To facet you plot by a single variable, use `facet_wrap()`. The first argument of should be a formula, which you create with `~` followed by a variable name (here "formula" is the name of a data structure in R, not a synonym for "equation"). The variable that you pass to `facet_wrap()` should be discrete.
Here we create a separate subplot for each level of the `clarity` variable. The first subplot displays the group of points that have the `clarity` value `I1`. The second subplot displays the group of points that have the `clarity` value `SI2`. And so on.
```{r fig.width = 7, out.width = "100%"}
```{r}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
facet_wrap(~ class, nrow = 2)
@ -233,17 +247,27 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
To facet your plot on the combination of two variables, add `facet_grid()` to your plot call. The first argument of `facet_grid()` is also a formula. This time the formula should contain two variable names separated by a `~`.
```{r fig.height = 7, out.width = "100%"}
```{r}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
facet_grid(drv ~ cyl)
```
If you prefer to not facet on the rows or columns dimension, place a `.` instead of a variable name before or after the `~`, e.g. `+ facet_grid(. ~ clarity)`.
If you prefer to not facet in the rows or columns dimension, use a `.` instead of a variable name, e.g. `+ facet_grid(. ~ clarity)`.
### Exercises
1. What plots will the following code make? What does `.` do?
1. What happens if you facet on a continuous variable?
1. What do the empty cells in plot with `facet_grid(drv ~ cyl)` mean?
How do they relate to this plot?
```{r, eval = FALSE}
ggplot(data = mpg) +
geom_point(mapping = aes(x = drv, y = cyl))
```
1. What plots does the following code make? What does `.` do?
```{r eval = FALSE}
ggplot(data = mpg) +
@ -257,24 +281,22 @@ If you prefer to not facet on the rows or columns dimension, place a `.` instead
1. Take the first faceted plot in this section:
```{r}
```{r, eval = FALSE}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
facet_wrap(~ class, nrow = 2)
```
What are the advantages to using facetting instead of the colour aesthetic?
What are the disadvantages? How might the balance of the two change if you
had a larger dataset?
What are the disadvantages? How might the balance change if you had a
larger dataset?
1. Read `?facet_wrap`. What does `nrows` do? What does `ncols` do? What other
options control the layout of the individual panels?
1. Read `?facet_wrap`. What does `nrow` do? What does `ncol` do? What other
options control the layout of the individual panels? Why doesn't
`facet_grid()` have `nrow` and `ncol` variables?
1. When using `facet_grid()` you should usually put the variable with more
unique levels in the columns. Why?
1. How might `cut_number()` and `cut_width()` help if you wanted to facet
by a continuous variable?
## Geometric objects
@ -288,20 +310,18 @@ ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy))
```
Both plots contain the same x variable, the same y variable, and both describe the same data. But the plots are not identical. Each plot uses a different visual object to represent the data. In ggplot2 syntax, we say that they use different _geoms_.
Both plots contain the same x variable, the same y variable, and both describe the same data. But the plots are not identical. Each plot uses a different visual object to represent the data. In ggplot2 syntax, we say that they use different __geoms__.
A _geom_ is the geometrical object that a plot uses to represent data. People often describe plots by the type of geom that the plot uses. For example, bar charts use bar geoms, line charts use line geoms, boxplots use boxplot geoms, and so on. Scatterplots break the trend; they use the point geom. As we see above, you can use different geoms to plot the same data. The plot on the left uses the point geom, and the plot on the right uses the smooth geom, a smooth line fitted to the data.
A __geom__ is the geometrical object that a plot uses to represent data. People often describe plots by the type of geom that the plot uses. For example, bar charts use bar geoms, line charts use line geoms, boxplots use boxplot geoms, and so on. Scatterplots break the trend; they use the point geom. As we see above, you can use different geoms to plot the same data. The plot on the left uses the point geom, and the plot on the right uses the smooth geom, a smooth line fitted to the data.
To change the geom in your plot, change the geom function that you add to `ggplot()`. For instance, to make the plot on the left, use `geom_point()`:
To change the geom in your plot, change the geom function that you add to `ggplot()`. For instance, to make the plots above, you can use this code:
```{r eval = FALSE}
# left
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))
```
To make the plot on the right use `geom_smooth()`:
```{r eval=FALSE, message = FALSE}
# right
ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy))
```
@ -325,11 +345,7 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) +
Notice that this plot contains two geoms in the same graph! If this makes you excited, buckle up. In the next section, we will learn how to place multiple geoms in the same plot.
ggplot2 provides over 30 geom functions that you can use to visualize your data, and extension packages provide even more. Each geom is particularly well suited for visualizing a certain type of data or a certain type of relationship. The table below lists the geoms in ggplot2, loosely organized by the type of relationship that they describe.
Next to each geom is a visual representation of the geom. Beneath the geom is a list of aesthetics that apply to the geom. Required aesthetics are listed in bold. Many geoms have very useful arguments that help them do their job. For these geoms, we've listed those arguments in the example code.
To learn more about any single geom, open it's help page in R by running the command `?` followed by the name of the geom function, e.g. `?geom_smooth`.
ggplot2 provides over 30 geoms, and extension packages provide even more (see <https://www.ggplot2-exts.org> for a sampling). The table below lists the geoms in ggplot2, loosely organized by the type of relationship that they visualise. Beneath each geom is a list of aesthetics the geom understands, and mandatory aesthetics are bolded. The geom call lists the most important arguments. To learn more about any single geom, open its help page in R by running the command `?` followed by the name of the geom function, e.g. `?geom_smooth`.
```{r, echo = FALSE, out.width = "100%"}
knitr::include_graphics("images/visualization-geoms-1.png")
@ -338,23 +354,23 @@ knitr::include_graphics("images/visualization-geoms-3.png")
knitr::include_graphics("images/visualization-geoms-4.png")
```
Many geoms use a single object to describe all of the data. For example, `geom_smooth()` uses a single line. For these geoms, you can set the group aesthetic to a discrete variable to draw multiple objects. ggplot2 will draw a separate object for each unique value of the grouping variable.
Many geoms, like `geom_smooth()`, use a single geometric object to display multiple rows of data. For these geoms, you can set the `group` aesthetic to a discrete variable to draw multiple objects. ggplot2 will draw a separate object for each unique value of the grouping variable. In practice, ggplot2 will automatically group the data for these geoms whenever you map an aesthetic to a discrete variable (as in the `linetype` example). It is convenient to rely on this feature because the group aesthetic by itself does not add a legend or distinguishing features to the geoms.
In practice, ggplot2 will automatically group the data for these geoms whenever you map an aesthetic to a discrete variable (as in the `linetype` example). It is convenient to rely on this feature because the group aesthetic by itself does not add a legend or distinguishing features to the geoms.
```{r, fig.asp = 1, fig.width = 2.5, fig.align = 'default', out.width = "33%"}
ggplot(diamonds) +
geom_smooth(aes(x = carat, y = price))
```{r, fig.width = 3, fig.align = 'default', out.width = "33%"}
ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy))
ggplot(diamonds) +
geom_smooth(aes(x = carat, y = price, group = cut))
ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy, group = drv))
ggplot(diamonds) +
geom_smooth(aes(x = carat, y = price, color = cut), show.legend = FALSE)
ggplot(data = mpg) +
geom_smooth(
mapping = aes(x = displ, y = hwy, colour = drv),
show.legend = FALSE
)
```
To display multiple geoms in the same plot, add multiple geom functions to `ggplot()`! ggplot2 will add each geom as a new layer on top of the previous geoms.
To display multiple geoms in the same plot, add multiple geom functions to `ggplot()`:
```{r}
ggplot(data = mpg) +
@ -362,9 +378,9 @@ ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy))
```
To avoid redundancy, pay attention to your code when you use multiple geoms. Our code now calls `mapping = aes(x = displ, y = hwy)` twice. You can avoid this type of repetition by passing a set of mappings to `ggplot()`. ggplot2 will treat these mappings as global mappings that apply to each geom in the graph.
This, however, introduces some duplication in our code. Imagine if you wanted to change the y-axis to display `cty` instead of `hwy`. You'd need to change the variable in two places, and you might forget to update one. You can avoid this type of repetition by passing a set of mappings to `ggplot()`. ggplot2 will treat these mappings as global mappings that apply to each geom in the graph. In other words, this code will produce the same plot as the previous code:
```{r}
```{r, eval = FALSE}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
geom_smooth()
@ -378,7 +394,7 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_smooth()
```
You can use the same idea to specify different `data` for each layer. Here, our smooth line displays just a subset of the `mpg` dataset, the subcompact cars. The local data argument in `geom_smooth()` overrides the global data argument in `ggplot()` for the smooth layer only.
You can use the same idea to specify different `data` for each layer. Here, our smooth line displays just a subset of the `mpg` dataset, the subcompact cars. The local data argument in `geom_smooth()` overrides the global data argument in `ggplot()` for that layer only.
```{r}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
@ -386,22 +402,12 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_smooth(data = dplyr::filter(mpg, class == "subcompact"), se = FALSE)
```
(Remember, `dplyr::filter()` means call the `filter()` function from dplyr. You'll learn how it works in the next chapter.)
(Remember, `dplyr::filter()` calls the `filter()` function from the dplyr package. You'll learn how `filter()` works in the next chapter.)
### Exericses
1. What does `show.legend = FALSE` do? What happens if you remove it?
Why do you think we used it in the example above.
1. What does the `se` argument to `geom_smooth()` do? (Hint: look at
`?geom_smooth()`)
1. What sort of graphic does `geom_boxplot()` produce?
1. What geom would you use to generate a line plot?
1. Why might you want to use `geom_count()` when plotting `cty` vs
`hwy`?
1. What geom would you use to draw a line chart? A boxplot?
A histogram? An area chart?
1. Run this code in your head and predict what the output will look like.
Run the code in R and check your predictions.
@ -411,6 +417,11 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
geom_smooth(se = FALSE)
```
1. What does `show.legend = FALSE` do? What happens if you remove it?
Why do you think I used it in the example above.
1. What does the `se` argument to `geom_smooth()` do?
1. Will these two graphs look different? Why/why not?
@ -426,7 +437,13 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
1. Recreate the R code necessary to generate the following graphs.
```{r, echo = FALSE, fig.width = 4, out.width = "50%", fig.align = "default"}
```{r echo = FALSE, fig.width = 3, out.width = "50%", fig.align = "default"}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
geom_smooth(aes(group = drv), se = FALSE)
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_smooth(aes(group = drv), se = FALSE) +
geom_point()
ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) +
geom_point() +
geom_smooth(se = FALSE)
@ -434,11 +451,8 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(aes(color = drv)) +
geom_smooth(se = FALSE)
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(aes(group = drv)) +
geom_smooth(se = FALSE)
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_smooth(aes(linetype = drv), se = FALSE) +
geom_point(aes(color = drv))
geom_point(aes(color = drv)) +
geom_smooth(aes(linetype = drv), se = FALSE)
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(size = 4, colour = "white") +
geom_point(aes(colour = drv))
@ -446,27 +460,24 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
## Statical transformations
Bar charts are interesting because they reveal something subtle about plots. Consider a basic bar chart, as drawn with `geom_bar()`. This chart displays the total number of diamonds in the `diamonds` dataset, grouped by `cut`. The `diamonds` dataset comes in ggplot2 and contains information about ~54,000 diamonds, including the `price`, `carat`, `color`, `clarity`, and `cut` of each diamond. The chart shows that more diamonds are available with high quality cuts than with low quality cuts.
Next, lets take a look at a bar chart. Bar charts seem simple, but they are interesting because they reveal something subtle about plots. Consider a basic bar chart, as drawn with `geom_bar()`. The following chart displays the total number of diamonds in the `diamonds` dataset, grouped by `cut`. The `diamonds` dataset comes in ggplot2 and contains information about ~54,000 diamonds, including the `price`, `carat`, `color`, `clarity`, and `cut` of each diamond. The chart shows that more diamonds are available with high quality cuts than with low quality cuts.
```{r}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut))
```
On the x axis, the chart displays `cut`, a variable in `diamonds`. On the y axis, it displays count; but count is not a variable in `diamonds`! Where does count come from?
On the x axis, the chart displays `cut`, a variable from `diamonds`. On the y axis, it displays count, but count is not a variable in `diamonds`! Where does count come from? Many graphs, like scatterplots, plot the raw values of your dataset. Other graphs, like bar charts, calculate new values to plot:
Some graphs, like scatterplots, plot the raw values of your dataset. Other graphs, like bar charts, calculate new values to plot:
* **bar charts** and **histograms** bin your data and then plot bin counts,
* __bar charts__ and __histograms__ bin your data and then plot bin counts,
the number of points that fall in each bin.
* **smooth lines** fit a model to your data and then plot the model line.
* __smoothers__ fit a model to your data and then plot predictions from the
model.
* **boxplots** calculate the quartiles of your data and then plot the
* __boxplots__ calculate the quartiles of your data and then plot the
quartiles as a box.
* and so on.
ggplot2 calls the algorithm that a graph uses to calculate new values a __stat__, which is short for statistical transformation. Each geom in ggplot2 is associated with a default stat that it uses to calculate values to plot. The figure below describes how this process works with `geom_bar()`.
```{r, echo = FALSE, out.width = "100%"}
@ -479,50 +490,46 @@ A few geoms, like `geom_point()`, plot your raw data as it is. These geoms also
knitr::include_graphics("images/visualization-stat-point.png")
```
You can learn which stat a geom uses, as well as what variables it computes by visiting the geom's help page. For example, the help page of `geom_bar()` shows that it uses the count stat and that the count stat computes two new variables, `count` and `prop`. If you have an R session open you can verify this by running `?geom_bar` at the command line.
You can learn which stat a geom uses, as well as what variables it computes by visiting the geom's help page. For example, the help page of `geom_bar()` shows that it uses the count stat and that the count stat computes two new variables, `count` and `prop`.
Stats are the most subtle part of plotting because you do not see them in action. ggplot2 applies the transformation and stores the results behind the scenes. You only see the finished plot. Moreover, ggplot2 applies stats automatically, with a very intuitive set of defaults. As a result, you rarely need to adjust a geom's stat. However, you can do three things with a geom's stat if you wish to.
Stats are the most subtle part of plotting because you can't see them directly. ggplot2 applies the transformation and stores the results behind the scenes. You only see impact in the final plot. Generally, you don't need to think about stats: the defaults work away on your behalf to summarise your data as needed for a particular plot. However, there are two cases where you might need to know about it:
First, you can change the stat that the geom uses with the geom's stat argument. In the code below, I change the stat of `geom_bar()` from count (the default) to identity. This lets me map to the height of the bars to the raw values of a $y$ variable.
1. You might want to override the default stat. In the code below, I change
the stat of `geom_bar()` from count (the default) to identity. This lets
me map to the height of the bars to the raw values of a $y$ variable.
```{r}
demo <- tibble::tibble(
a = c("bar_1","bar_2","bar_3"),
b = c(20, 30, 40)
)
demo
ggplot(data = demo) +
geom_bar(mapping = aes(x = a, y = b), stat = "identity")
```
(Unfortunately when people talk about bar charts casually, they might be
referring to this type of bar chart, where the height of the bar is already
present in the data, or the previous bar chart where the height of the bar
is generated by counting rows.)
```{r}
demo <- tibble::tibble(
a = c("bar_1","bar_2","bar_3"),
b = c(20, 30, 40)
)
demo
ggplot(data = demo) +
geom_bar(mapping = aes(x = a, y = b), stat = "identity")
```
(Unfortunately when people talk about bar charts casually, they might be referring to this type of bar chart, where the height of the bar is already present in the data, or the previous bar chart where the height of the bar is generated by counting rows.)
Second, you can give some stats arguments by passing the arguments to your geom function. We saw one earlier when we passed `se = FALSE` to `geom_smooth()`, telling it not to calculate and display the standard errors. In the code below, I pass a width argument to the count stat, which controls the widths of the bars. `width = 1` will make the bars wide enough to touch each other.
```{r}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut), width = 1)
```
You can learn which arguments a stat takes at the stat's help page. To open the help page, place the prefix `?stat_` before the name of the stat, then run the command at the command line, e.g. `?stat_count`. Often the geom and stat will have the same documentation page so you don't need to jump around.
Finally, you can use extra variables created by the stat. Many stats in ggplot2 create multiple variables, some of which go unused. For example, `geom_count()` uses the "sum" stat to create bubble charts. Each bubble represents a group of data points, and the size of the bubble displays how many points are in the group (e.g. the count of the group).
```{r}
ggplot(data = diamonds) +
geom_count(mapping = aes(x = cut, y = clarity))
```
The help page of `?stat_sum` reveals that the sum stat creates two variables, n (count) and prop. By default, `geom_count()` uses the n variable to create the size of each bubble. To tell `geom_count()` to use the prop variable, map $size$ to `..prop..`. The two dots that surround prop notify ggplot2 that the prop variable appears in the transformed dataset that is created by the stat, and not in the raw dataset. Be sure to include these dots whenever you refer to a variable that is created by a stat.
```{r}
ggplot(data = diamonds) +
geom_count(mapping = aes(x = cut, y = clarity, size = ..prop.., group = clarity))
```
For `geom_count()`, the `..prop..` variable does not do anything useful until you set a group aesthetic. If you set _group_ to the $x$ variable, `..prop..` will show proportions across columns. If you set it to the $y$ variable, `..prop..` will show proportions across rows, as in the plot above. Here, the proportions in each row sum to one.
1. You might want to override the default mapping from transformed variables
to aesthetics. For example, you might want to display a bar chart of
proportion, rather than count:
```{r}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, y = ..prop.., group = 1))
```
The help page of `?geom_bar` reveals that the sum stat creates two
variables, `count` and `prop`. By default, `geom_bar()` maps `y`
to `count`, but you can ask it to use `prop` instead with
`aes(y = ..prop..)`.The two dots that surround prop notify ggplot2 that
the `prop` variable appears in the transformed dataset not in the
raw dataset.
ggplot2 provides over 20 stats for you to use. Each stat is saved as a function, which provides a convenient way to access a stat's help page, e.g. `?stat_identity`. The table below describes each stat in ggplot2 and lists the parameters that the stat takes, as well as the variables that the stat makes.
```{r, echo = FALSE, out.width = "100%"}
@ -531,44 +538,51 @@ knitr::include_graphics("images/visualization-stats.png")
### Exercises
1. In our proportion barchart, we need to set `group = 1`. Why? In other
words, why is this graph not useful?
```{r, eval = FALSE}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, y = ..prop..))
```
1. How do you find out the default stat associated with a geom?
## Position adjustments
If you want to color the inside of bars, you need to use the `fill` aesthetic rather than `colour`:
There's one more piece of magic associated with bar charts. You can colour bar chart using either the `colour` aesthetic, or more usefully, `fill`:
```{r fig.width = 4, out.width = "50%", fig.align = "default"}
```{r fig.width = 3, out.width = "50%", fig.align = "default"}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, colour = cut))
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = cut))
```
If you map the fill aesthetic to another variable, like `clarity`, you get a stacked bar chart. Each colored rectangle represents a combination of `cut` and `clarity`.
Note what happens if you mapped the fill aesthetic to another variable, like `clarity`: the bars are automatically stacked. Each colored rectangle represents a combination of `cut` and `clarity`.
```{r}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = clarity))
```
But what if you don't want a stacked bar chart? You can control how ggplot2 deals with overlapping bars using a __position adjustment__ specified by the `position` argument. There are four important options for bars:`"identity"`, `"stack"`, `"dodge"` and `"fill"`.
The stacking is performed automatically by the __position adjustment__ specified by the `position` argument. If you don't want a stacked bar chart, you can use one of three other options: `"identity"`, `"dodge"` or `"fill"`.
* `position = "identity"` will place each object exactly where it falls in
the context of the graph. This is not very useful for bars, because it
overlaps them. To see that overlapping we either need to make the bars
slightly transparent by setting `alpha`, or completely transparent.
slightly transparent by setting `alpha` to a small value, or completely
transparent by setting `fill = NA`.
```{r, fig.width = 4, out.width = "50%", fig.align = "default"}
```{r fig.width = 3, out.width = "50%", fig.align = "default"}
ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) +
geom_bar(alpha = 1/5, position = "identity")
ggplot(data = diamonds, mapping = aes(x = cut, colour = clarity)) +
geom_bar(fill = NA, position = "identity")
```
The identity position adjustment is more useful for 2d geoms, where it
is the default.
* `position = "stack"` is the default position adjustment for bars, as
you've seen above.
The identity position adjustment is more useful for 2d geoms, like points,
where it is the default.
* `position = "fill"` work like stacking, but makes each set of stacked bars
the same height. This makes it easier to compare proportions across
@ -576,8 +590,7 @@ But what if you don't want a stacked bar chart? You can control how ggplot2 deal
```{r}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = clarity), position = "fill") +
ggtitle('Position = "fill"')
geom_bar(mapping = aes(x = cut, fill = clarity), position = "fill")
```
* `position = "dodge"` places overlapping objects directly _beside_ one
@ -585,24 +598,17 @@ But what if you don't want a stacked bar chart? You can control how ggplot2 deal
```{r}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = clarity), position = "dodge") +
ggtitle('Position = "dodge"')
geom_bar(mapping = aes(x = cut, fill = clarity), position = "dodge")
```
***
**Tip** - You can add a title to your plot by adding `+ ggtitle("<Your Title>")` to your plot call.
***
These last type of position adjustment does not make sense for bar charts, but it can be very useful for scatterplots. Recall our first scatterplot. Did you notice that the plot displays only 126 points, even though there are 234 observations in the dataset?
There's one other type of adjustment that's not useful for bar charts, but it can be very useful for scatterplots. Recall our first scatterplot. Did you notice that the plot displays only 126 points, even though there are 234 observations in the dataset?
```{r echo = FALSE}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))
```
The values of `hwy` and `displ` are rounded to the nearest integer and tenths respectively. As a result, the points appear on a grid and many points overlap each other. This arrangement makes it hard to see where the mass of the data is. Are the data points spread equally throughout the graph, or is there one special combination of `hwy` and `displ` that contains 109 values?
The values of `hwy` and `displ` are rounded so the points appear on a grid and many points overlap each other. This problem is known as __overplotting__. This arrangement makes it hard to see where the mass of the data is. Are the data points spread equally throughout the graph, or is there one special combination of `hwy` and `displ` that contains 109 values?
You can avoid this gridding by setting the position adjustment to "jitter". `position = "jitter"` adds a small amount of random noise to each point. This spreads the points out because no two points are likely to receive the same amount of random noise.
@ -627,6 +633,9 @@ To learn more about a position adjustment, look up the help page associated with
1. Compare and contrast `geom_jitter()` with `geom_count()`.
1. What's the default position adjustment for `geom_boxplot()`? Create
a visualisation of the `mpg` dataset that demonstrates it.
## Coordinate systems
Coordinate systems are probably the most complicated part of ggplot2. The default coordinate system is the Cartesian coordinate system where the x and y position act independently to find the location of each point.
@ -636,7 +645,9 @@ There are a number of other coordinate systems that are occassionally helpful.
* `coord_flip()` switches the x and y axes. This is useful (for example),
if you want vertical boxplots.
```{r, asp = 1.61}
```{r fig.width = 3, out.width = "50%", fig.align = "default"}
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
geom_boxplot()
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
geom_boxplot() +
coord_flip()
@ -646,7 +657,7 @@ There are a number of other coordinate systems that are occassionally helpful.
important if you're plotting spatial data with ggplot2 (which unfortunately
we don't have the space to cover in this book).
```{r fig.width=3, out.width = "50%", fig.align = "default"}
```{r fig.width = 3, out.width = "50%", fig.align = "default", message = FALSE}
nz <- map_data("nz")
ggplot(nz, aes(long, lat, group = group)) +
@ -660,16 +671,22 @@ There are a number of other coordinate systems that are occassionally helpful.
* `coord_polar()` uses polar coordinates. Polar coordinates reveals an
interesting connection between a bar chart and a Coxcomb chart.
```{r fig.width=3, out.width = "50%", fig.align = "default"}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = cut)) +
theme(aspect.ratio = 1)
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = cut), width = 1) +
coord_polar()
```{r fig.width = 3, out.width = "50%", fig.align = "default", fig.asp = 1}
bar <- ggplot(data = diamonds) +
geom_bar(
mapping = aes(x = cut, fill = cut),
show.legend = FALSE,
width = 1
) +
theme(aspect.ratio = 1) +
xlab(NULL) +
ylab(NULL)
bar + coord_flip()
bar + coord_polar()
```
The table below describes each function and what it does. Add any of these functions to your plot's call to change the coordinate system that the plot uses. You can learn more about each coordinate system by opening its help page in R, e.g. `?coord_cartesian`, `?coord_fixed`, `?coord_flip`, `?coord_map`, `?coord_polar`, and `?coord_trans`.
The table below describes each built-in coord. You can learn more about each coordinate system by opening its help page in R, e.g. `?coord_cartesian`.
```{r, echo = FALSE, out.width = "100%"}
knitr::include_graphics("images/visualization-coordinate-systems.png")
@ -682,10 +699,10 @@ knitr::include_graphics("images/visualization-coordinate-systems.png")
1. What's the difference between `coord_quickmap()` and `coord_map()`?
1. What does the plot below tell you about the relationship between city
and highway mpg? Why is `coord_fixed()` important? What does `geom_abline()`
do?
and highway mpg? Why is `coord_fixed()` important? What does
`geom_abline()` do?
```{r}
```{r, fig.asp = 1, out.width = "50%"}
ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
geom_point() +
geom_abline() +