Tweaks to visualisation

This commit is contained in:
hadley 2016-07-11 17:22:37 -05:00
parent 594f663e67
commit 8bb4165720
1 changed files with 172 additions and 143 deletions

View File

@ -1,56 +1,77 @@
# Data visualisation
> "The simple graph has brought more information to the data analysts mind than any other device."---John Tukey
> "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 the `ggplot2` package. 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 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.
### Prerequisites
## Prerequisites
To access the datasets, help pages, and functions that we will use in this chapter, load the `ggplot2` package:
To access the datasets, help pages, and functions that we will use in this chapter, load ggplot2. We'll also load tibble, which you'll learn about later. It improves the default printing of datasets.
```{r echo = FALSE, message = FALSE, warning = FALSE}
```{r setup}
library(ggplot2)
library(tibble)
```
If you run this code and get the error `there is no package called ggplot2`, you'll need to first install it, and then run `library()` once again.
```{r eval = FALSE}
install.packages("ggplot2")
library(ggplot2)
```
## A code template
You only need to install a package once, but you need to reload it every time you start a new session.
TODO: mention missing values.
## A graphing template
Let's use our first graph to answer a question: Do cars with big engines use more fuel than cars with small engines? You probably already have an answer, but try to make your answer precise. What does the relationship between engine size and fuel efficiency look like? Is it positive? Negative? Linear? Nonlinear?
You can test your answer with the `mpg` dataset in the `ggplot2` package. The dataset contains observations collected by the EPA on 38 models of car. Among the variables in `mpg` are
You can test your answer with the `mpg` dataset in ggplot2, or `ggplot2::mpg`:
```{r}
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
2. `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.
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 plot `mpg`, open an R session and run the code below. The code plots the `displ` variable of `mpg` against the `hwy` variable.
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:
```{r}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))
```
The plot shows a negative relationship between engine size (`displ`) and fuel efficiency (`hwy`). In other words, cars with big engines use more fuel. Does this confirm your hypothesis about fuel efficiency and engine size?
The plot shows a negative relationship between engine size (`displ`) and fuel efficiency (`hwy`). In other words, cars with big engines use more fuel. Does this confirm or refute your hypothesis about fuel efficiency and engine size?
Pay close attention to this code because it is almost a template for making plots with `ggplot2`.
Pay close attention to this code because it is almost a template for making plots with ggplot2.
```{r eval=FALSE}
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 that will use the `mpg` dataset:
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.
```{r}
ggplot(data = mpg)
```
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`.
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.
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.
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`.
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.
```{r eval = FALSE}
ggplot(data = <DATA>) +
@ -61,7 +82,8 @@ The rest of this chapter will show you how to complete and extend this template
## Aesthetic mappings
> "The greatest value of a picture is when it forces us to notice what we never expected to see."---John Tukey
> "The greatest value of a picture is when it forces us to notice what we
> never expected to see." --- John Tukey
In the plot below, one group of points seems to fall outside of the linear trend. These cars have a higher mileage than you might expect. How can you explain these cars?
@ -86,13 +108,15 @@ ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, color = class))
```
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.
(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.
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.
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.
```{r warning=FALSE}
```{r}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, size = class))
```
@ -111,11 +135,11 @@ 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 because you also set `x` and `y` to variables within `aes()`. The insight is that 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, 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.
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 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.
You can also set the aesthetic properties of your geom manually. For example, we can make all of the points in our plot blue.
@ -126,68 +150,65 @@ ggplot(data = mpg) +
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
* the name of a color as a character string
* the size of a point as a cex expansion factor (see `?par`)
* the shape as a point as a number code
* 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 echo=FALSE}
pchShow <-
function(extras = c("*",".", "o","O","0","+","-","|","%","#"),
cex = 2,
col = "red3", bg = "gold", coltext = "brown", cextext = 1.1,
main = "")
{
nex <- length(extras)
np <- 26 + nex
ipch <- 0:(np-1)
k <- floor(sqrt(np))
dd <- c(-1,1)/2
rx <- dd + range(ix <- ipch %/% k)
ry <- dd + range(iy <- 3 + (k-1)- ipch %% k)
pch <- as.list(ipch) # list with integers & strings
if(nex > 0) pch[26+ 1:nex] <- as.list(extras)
plot(rx, ry, type = "n", axes = FALSE, xlab = "", ylab = "", main = main)
abline(v = ix, h = iy, col = "lightgray", lty = "dotted")
for(i in 1:np) {
pc <- pch[[i]]
points(ix[i], iy[i], pch = pc, col = col, bg = bg, cex = cex)
if(cextext > 0)
text(ix[i] - 0.4, iy[i], pc, col = coltext, cex = cextext)
}
}
pchShow()
shapes <- tibble(
shape = c(0:19, 22, 21, 24, 23, 20),
x = 0:24 %/% 5,
y = -(0:24 %% 5)
)
ggplot(shapes, aes(x, y)) +
geom_point(aes(shape = shape), size = 5, fill = "red") +
geom_text(aes(label = shape), hjust = 0, nudge_x = 0.15) +
scale_shape_identity() +
expand_limits(x = 4.1) +
scale_x_continuous(NULL, breaks = NULL) +
scale_y_continuous(NULL, breaks = NULL)
```
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 set the aesthetic:
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.
* _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
Now that you know how to use aesthetics, take a moment to experiment with the `mpg` dataset.
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`, `alpha`, and `shape`. Then map a continuous variable to each. Does `ggplot2` behave differently for discrete vs. continuous variables?
+ The discrete variables in `mpg` are: `manufacturer`, `model`, `trans`, `drv`, `fl`, `class`
+ The continuous variables in `mpg` are: `displ`, `year`, `cyl`, `cty`, `hwy`
2. Map the same variable to multiple aesthetics in the same plot. Does it work? How many legends does `ggplot2` create?
3. Attempt to set an aesthetic to something other than a variable name, like `displ < 5`. What happens?
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?
**Tip** - See the help page for `geom_point()` (by running `?geom_point`) to learn which aesthetics are available to use in a scatterplot. See the help page for the `mpg` dataset (`?mpg`) to learn which variables are in the dataset.
***
```{r}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, color = "blue"))
```
## Geoms
How are these two plots similar?
```{r echo = FALSE, message = FALSE, fig.show='hold', fig.width=3, fig.height=3}
```{r echo = FALSE, out.width = "50%"}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))
@ -195,7 +216,7 @@ 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.
@ -213,7 +234,7 @@ ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy))
```
Every geom function in `ggplot2` takes a `mapping` argument. However, not every aesthetic works with every geom. You could set the shape of a point, but you couldn't set the "shape" of a line. On the other hand, you _could_ set the linetype of a line. `geom_smooth()` will draw a different line, with a different linetype, for each unique value of the variable that you map to linetype.
Every geom function in ggplot2 takes a `mapping` argument. However, not every aesthetic works with every geom. You could set the shape of a point, but you couldn't set the "shape" of a line. On the other hand, you _could_ set the linetype of a line. `geom_smooth()` will draw a different line, with a different linetype, for each unique value of the variable that you map to linetype.
```{r message = FALSE}
ggplot(data = mpg) +
@ -224,15 +245,15 @@ Here `geom_smooth()` separates the cars into three lines based on their `drv` va
If this sounds strange, we can make it more clear by overlaying the lines on top of the raw data and then coloring everything according to `drv`.
```{r message = FALSE, echo = FALSE}
```{r echo = FALSE}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = drv)) +
geom_point() +
geom_smooth(mapping = aes( linetype = drv))
geom_smooth(mapping = aes(linetype = 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 37 geom functions that you can use to visualize your data. 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.
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.
@ -240,25 +261,16 @@ To learn more about any single geom, open it's help page in R by running the com
```{r, echo = FALSE}
knitr::include_graphics("images/visualization-geoms-1.png")
```
```{r, echo = FALSE}
knitr::include_graphics("images/visualization-geoms-2.png")
```
```{r, echo = FALSE}
knitr::include_graphics("images/visualization-geoms-3.png")
```
```{r, echo = FALSE}
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 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.
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.show='hold', fig.height = 2.5, fig.width = 2.5}
```{r, fig.show='hold', fig.height = 2.5, fig.width = 2.5, out.width = "33%"}
ggplot(diamonds) +
geom_smooth(aes(x = carat, y = price))
@ -266,30 +278,45 @@ ggplot(diamonds) +
geom_smooth(aes(x = carat, y = price, group = cut))
ggplot(diamonds) +
geom_smooth(aes(x = carat, y = price, color = cut))
geom_smooth(aes(x = carat, y = price, color = cut), show.legend = FALSE)
```
### 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`?
## Layers
To display multiple geoms in the same plot, add multiple geom functions to `ggplot()`. `ggplot2` will add each new 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()`. ggplot2 will add each new geom as a new layer on top of the previous geoms.
```{r, message = FALSE}
```{r}
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
geom_smooth(mapping= aes(x = displ, y = hwy))
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. You can then remove the mapping arguments in the individual layers.
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. You can then remove the mapping arguments in the individual layers.
```{r, message = FALSE}
```{r}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
geom_smooth()
```
If you place mappings in a geom function, `ggplot2` will treat them as local mappings for the layer. It will use these mappings to extend or overwrite the global mappings _for that layer only_. This provides an easy way to differentiate layers.
If you place mappings in a geom function, ggplot2 will treat them as local mappings for the layer. It will use these mappings to extend or overwrite the global mappings _for that layer only_. This provides an easy way to differentiate layers.
```{r, message = FALSE}
```{r}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(mapping = aes(color = class)) +
geom_smooth()
@ -297,45 +324,49 @@ ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
You can use the same system to specify individual datasets 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.
```{r, message = FALSE, warning = FALSE}
```{r}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point(aes(color = class)) +
geom_smooth(data = subset(mpg, class == "subcompact"))
geom_smooth(data = dplyr::filter(mpg, class == "subcompact"))
```
(You'll learn how `dplyr::filter()` works in the next chapter.)
### Exercises
1. What would this graph look like?
1. What would this graph look like?
```{r, eval = FALSE}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = class)) +
geom_point() +
geom_smooth()
```
```{r, eval = FALSE}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy, color = class)) +
geom_point() +
geom_smooth()
```
1. Will these two graphs look different? Why/why not?
2. Will these two graphs look different?
```{r, eval = FALSE}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
geom_smooth()
ggplot(mapping = aes(x = displ, y = hwy)) +
geom_point(data = mpg) +
geom_smooth(data = mpg)
```
```{r, eval = FALSE}
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point() +
geom_smooth()
ggplot(mapping = aes(x = displ, y = hwy)) +
geom_point(data = mpg) +
geom_smooth(data = mpg)
```
1. Recreate the R code necessary to generate the following graphs.
TODO: add examples.
## Position adjustments
To make a bar chart with `ggplot2` use the function `geom_bar()`. `geom_bar()` does not require a $y$ aesthetic.
To make a bar chart with ggplot2 use the function `geom_bar()`. `geom_bar()` does not require a $y$ aesthetic.
```{r}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut))
```
The chart above displays the total number of diamonds in the `diamonds` dataset, grouped by `cut`. The `diamonds` dataset comes in `ggplot2` and contains information about 53,940 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.
The chart above 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.
A bar has different visual properties than a point, which can create some surprises. For example, how would you create this simple chart? If you have an R session open, give it a try.
@ -351,7 +382,7 @@ ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, color = cut))
```
The effect is interesting, sort of psychedelic, but not what we had in mind. To control the interior fill of a bar, you must call the _fill_ aesthetic. The same pattern applies for all geoms that contain "substance." _Color_ controls the outline of the geom and _fill_ controls the interior fill.
The effect is interesting, and sort of psychedelic, but not what we had in mind. To control the interior fill of a bar, you must call the _fill_ aesthetic. The same pattern applies for all geoms that contain "substance." _Color_ controls the outline of the geom and _fill_ controls the interior fill.
```{r}
ggplot(data = diamonds) +
@ -372,13 +403,13 @@ ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = clarity), position = "dodge")
```
The chart displays the same 40 color coded rectangles as the stacked bar chart above. However, the position of the bars within the two charts is different. In the stacked bar chart, `ggplot2` stacked bars that have the same `cut` value on top of each other. In this plot, `ggplot2` places bars that have the same `cut` value beside each other.
The chart displays the same 40 color coded rectangles as the stacked bar chart above. However, the position of the bars within the two charts is different. In the stacked bar chart, ggplot2 stacked bars that have the same `cut` value on top of each other. In this plot, ggplot2 places bars that have the same `cut` value beside each other.
You can control this behavior by adding a _position adjustment_ to your geom. A position adjustment tells `ggplot2` what to do when two or more objects appear at the same spot in the coordinate system. To set a position adjustment, set the `position` argument of your geom function to one of `"identity"`, `"stack"`, `"dodge"`, `"fill"`, or `"jitter"`.
You can control this behavior by adding a _position adjustment_ to your geom. A position adjustment tells ggplot2 what to do when two or more objects appear at the same spot in the coordinate system. To set a position adjustment, set the `position` argument of your geom function to one of `"identity"`, `"stack"`, `"dodge"`, `"fill"`, or `"jitter"`.
### Position = "identity"
When `position = "identity"`, `ggplot2` will place each object exactly where it falls in the context of the graph.
When `position = "identity"`, ggplot2 will place each object exactly where it falls in the context of the graph.
For our bar chart, this would mean that each bar would start at `y = 0` and would appear directly above the `cut` value that it describes. Since there are eight bars for each value of `cut`, many bars would overlap. The plot will look suspiciously like a stacked bar chart, but the stacked heights will be inaccurate, as each bar actually descends to `y = 0`. Some bars would not appear at all because they would be completely overlapped by other bars.
@ -399,7 +430,7 @@ ggplot(data = diamonds) +
### Position = "stack"
`position = "stack"` places overlapping objects directly _above_ one another. This is the default position adjustment for bar charts in `ggplot2`. Here each bar begins exactly where the bar below it ends.
`position = "stack"` places overlapping objects directly _above_ one another. This is the default position adjustment for bar charts in ggplot2. Here each bar begins exactly where the bar below it ends.
```{r}
ggplot(data = diamonds) +
@ -448,7 +479,7 @@ ggplot(data = mpg) +
This may seem like a bad idea since jittering will make your graph less accurate at the local level, but jittering may make your graph _more_ revealing at the global level. Occasionally, jittering will reveal a pattern that was hidden within the grid.
`ggplot2` comes with a special geom `geom_jitter()` that is the exact equivalent of `geom_point(position = "jitter")`.
ggplot2 comes with a special geom `geom_jitter()` that is the exact equivalent of `geom_point(position = "jitter")`.
To learn more about a position adjustment, look up the help page associated with each adjustment: `?position_dodge`, `?position_fill`, `?position_identity`, `?position_jitter`, and `?position_stack`.
@ -464,19 +495,24 @@ ggplot(data = diamonds) +
On the x axis, the chart displays `cut`, a variable in the `diamonds` dataset. On the y axis, it displays count; but count is not a variable in the diamonds dataset:
```{r}
head(diamonds)
diamonds
```
Where does count come from?
Some 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,
the number of points that fall in each bin.
* **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.
* **boxplots** calculate the quartiles of your data and then plot the quartiles as a box.
* **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()`.
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}
knitr::include_graphics("images/visualization-stat-bar.png")
@ -484,14 +520,13 @@ knitr::include_graphics("images/visualization-stat-bar.png")
A few geoms, like `geom_point()`, plot your raw data as it is. These geoms also apply a transformation to your data, the identity transformation, which returns the data in its original state. Now we can say that _every_ geom uses a stat.
```{r, echo = FALSE}
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.
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 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.
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 the height of the bars to the raw values of a $y$ variable.
@ -507,7 +542,7 @@ ggplot(data = demo) +
demo
```
I provide a list of the stats that are available to use in ggplot2 at the end of this section. Be careful when you change a geom's stat. Many combinations of geoms and stats will create incompatible results. In practice, you will almost always use a geom's default stat.
(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. 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.
@ -516,16 +551,16 @@ 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`.
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).
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.
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) +
@ -534,11 +569,7 @@ ggplot(data = diamonds) +
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.
In most cases, you will not want to switch the default variable supplied by a stat. Many stats only return one useful variable. The best way to discover which variables are created by a stat is to visit the stat's help page.
`ggplot2` provides 22 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.
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}
knitr::include_graphics("images/visualization-stats.png")
@ -548,7 +579,7 @@ knitr::include_graphics("images/visualization-stats.png")
Let's leave the Cartesian coordinate system and examine the polar coordinate system. We will begin with a riddle: how is a bar chart similar to a coxcomb plot, like the one below?
```{r echo = FALSE, message = FALSE, fig.show='hold', fig.width=3, fig.height=4}
```{r echo = FALSE, fig.show='hold', fig.width=3, fig.height=4, out.width = "50%"}
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = cut))
ggplot(data = diamonds) +
@ -556,7 +587,7 @@ ggplot(data = diamonds) +
coord_polar()
```
A coxcomb plot is a bar chart plotted in polar coordinates. If this seems surprising, consider how you would make a coxcomb plot with `ggplot2`.
A coxcomb plot is a bar chart plotted in polar coordinates. If this seems surprising, consider how you would make a coxcomb plot with ggplot2.
To make a coxcomb plot, first build a bar chart and then add `coord_polar()` to your plot call. Polar bar charts will look better if you also set the width parameter of `geom_bar()` to 1. This will ensure that no space appears between the bars.
@ -566,9 +597,9 @@ ggplot(data = diamonds) +
coord_polar()
```
You can use `coord_polar()` to turn any plot in `ggplot2` into a polar chart. Whenever you add `coord_polar()` to a plot's call, `ggplot2` will draw the plot on a polar coordinate system. It will map the plot's $y$ variable to $r$ and the plot's $x$ variable to $\theta$. You can reverse this behavior by passing `coord_polar()` the argument `theta = "y"`.
You can use `coord_polar()` to turn any plot in ggplot2 into a polar chart. Whenever you add `coord_polar()` to a plot's call, ggplot2 will draw the plot on a polar coordinate system. It will map the plot's $y$ variable to $r$ and the plot's $x$ variable to $\theta$. You can reverse this behavior by passing `coord_polar()` the argument `theta = "y"`.
Polar coordinates unlock another riddle as well. You may have noticed that `ggplot2` does not come with a pie chart geom. In practice, a pie chart is a stacked bar chart plotted in polar coordinates. To make a pie chart in `ggplot2`, create a stacked bar chart and:
Polar coordinates unlock another riddle as well. You may have noticed that ggplot2 does not come with a pie chart geom. In practice, a pie chart is a stacked bar chart plotted in polar coordinates. To make a pie chart in ggplot2, create a stacked bar chart and:
1. ensure that the x axis only has one value. An easy way to do this is to set `x = factor(1)`.
2. set the width of the bar to one, e.g. `width = 1`
@ -581,7 +612,7 @@ ggplot(data = diamonds) +
coord_polar(theta = "y")
```
`ggplot2` comes with eight coordinate functions that you can use in the same way as `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.
ggplot2 comes with eight coordinate functions that you can use in the same way as `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`.
@ -589,7 +620,6 @@ You can learn more about each coordinate system by opening its help page in R, e
knitr::include_graphics("images/visualization-coordinate-systems.png")
```
## Facets
Coxcomb plots are especially useful when you split your plot into _facets_, subplots that each display a subset of the data. Each subplot will act as a glyph that you can use to compare groups of data.
@ -616,7 +646,7 @@ Here the first subplot displays all of the points that have an `I1` code for `cl
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)`.
Faceting works on more than just polar charts. You can add `facet_wrap()` or `facet_grid()` to any plot in `ggplot2`.
Faceting works on more than just polar charts. You can add `facet_wrap()` or `facet_grid()` to any plot in ggplot2.
### Exercises
@ -636,12 +666,11 @@ Faceting works on more than just polar charts. You can add `facet_wrap()` or `fa
facet_grid(. ~ cyl)
```
## The layered grammar of graphics
In the previous sections, you learned much more than how to make scatterplots, bar charts, and coxcomb plots. You learned a foundation that you can use to make _any_ type of plot with `ggplot2`.
In the previous sections, you learned much more than how to make scatterplots, bar charts, and coxcomb plots. You learned a foundation that you can use to make _any_ type of plot with ggplot2.
To see this, add position adjustments, stats, coordinate systems, and faceting to our code template. In `ggplot2`, each of these parameters will work with every plot and every geom.
To see this, add position adjustments, stats, coordinate systems, and faceting to our code template. In ggplot2, each of these parameters will work with every plot and every geom.
```{r eval = FALSE}
ggplot(data = <DATA>) +
@ -654,7 +683,7 @@ ggplot(data = <DATA>) +
<FACET_FUNCTION>
```
Our new template takes seven parameters, the bracketed words that appear in the template. In practice, you rarely need to supply all seven parameters to make a graph because `ggplot2` will provide useful defaults for everything except the data, the mappings, and the geom function.
Our new template takes seven parameters, the bracketed words that appear in the template. In practice, you rarely need to supply all seven parameters to make a graph because ggplot2 will provide useful defaults for everything except the data, the mappings, and the geom function.
The seven parameters in the template compose the grammar of graphics, a formal system for building plots. The grammar of graphics is based on the insight that you can uniquely describe _any_ plot as a combination of a dataset, a geom, a set of mappings, a stat, a position adjustment, a coordinate system, and a faceting scheme.