We will not cover every single function and option for each of these layers, but we will walk you through the most important and commonly used functionality provided by ggplot2 as well as introduce you to packages that extend ggplot2.
Remember that the `mpg` data frame bundled with the ggplot2 package contains `r nrow(mpg)` observations on `r mpg |> distinct(model) |> nrow()` car models.
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.
A numerical variable.
3. `class`: Type of car.
A categorical variable.
Let's start by visualizing the relationship between `displ` and `hwy` for various `class`es of cars.
We can do this with a scatterplot where the numerical variables are mapped to the `x` and `y` aesthetics and the categorical variable is mapped to an aesthetic like `color` or `shape`.
```{r}
#| layout-ncol: 2
#| fig-width: 4
#| fig-alt: >
#| Two scatterplots next to each other, both visualizing highway fuel
#| efficiency versus engine size of cars and showing a negative
#| association. In the plot on the left class is mapped to the color
#| aesthetic, resulting in different colors for each class.
#| In the plot on the right class is mapped the shape aesthetic,
#| resulting in different plotting character shapes for each class,
#| except for suv. Each plot comes with a legend that shows the
#| mapping between color or shape and levels of the class variable.
# Left
ggplot(mpg, aes(x = displ, y = hwy, color = class)) +
Mapping an unordered discrete (categorical) variable (`class`) to an ordered aesthetic (`size` or `alpha`) is generally not a good idea because it implies a ranking that does not in fact exist.
You can also set the visual properties of your geom manually as an argument of your geom function (*outside* of `aes()`) instead of relying on a variable mapping to determine the appearance.
So far we have discussed aesthetics that we can map or set in a scatterplot, when using a point geom.
You can learn more about all possible aesthetic mappings in the aesthetic specifications vignette at <https://ggplot2.tidyverse.org/articles/ggplot2-specs.html>.
The specific aesthetics you can use for a plot depend on the geom you use to represent the data.
In the next section we dive deeper into geoms.
### Exercises
1. Create a scatterplot of `hwy` vs. `displ` where the points are pink filled in triangles.
2. Why did the following code not result in a plot with blue points?
Here, `geom_smooth()` separates the cars into three lines based on their `drv` value, which describes a car's drive train.
One line describes all of the points that have a `4` value, one line describes all of the points that have an `f` value, and one line describes all of the points that have an `r` value.
Here, `4` stands for four-wheel drive, `f` for front-wheel drive, and `r` for rear-wheel drive.
#| A plot of highway fuel efficiency versus engine size of cars. The data
#| are represented with points (colored by drive train) as well as smooth
#| curves (where line type is determined based on drive train as well).
#| Confidence intervals around the smooth curves are also displayed.
ggplot(mpg, aes(x = displ, y = hwy, color = drv)) +
geom_point() +
geom_smooth(aes(linetype = drv))
```
Notice that this plot contains two geoms in the same graph.
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 categorical 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.
#| Scatterplot of highway fuel efficiency versus engine size of cars, where
#| points are colored according to the car class. A smooth curve following
#| the trajectory of the relationship between highway fuel efficiency versus
#| engine size of subcompact cars is overlaid along with a confidence interval
#| around it.
ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point() +
geom_point(
data = mpg |> filter(class == "2seater"),
color = "red"
) +
geom_point(
data = mpg |> filter(class == "2seater"),
shape = "circle open", size = 3, color = "red"
)
```
Geoms are the fundamental building blocks of ggplot2.
You can completely transform the look of your plot by changing its geom, and different geoms can reveal different features of your data.
For example, the histogram and density plot below reveal that the distribution of highway mileage is bimodal and right skewed while the boxplot reveals two potential outliers.
ggplot2 provides more than 40 geoms but these don't cover all possible plots one could make.
If you need a different geom, we recommend looking into extension packages first to see if someone else has already implemented it (see <https://exts.ggplot2.tidyverse.org/gallery/> for a sampling).
For example, the **ggridges** package ([https://wilkelab.org/ggridges](https://wilkelab.org/ggridges/){.uri}) is useful for making ridgeline plots, which can be useful for visualizing the density of a numerical variable for different levels of a categorical variable.
In the following plot not only did we use a new geom (`geom_density_ridges()`), but we have also mapped the same variable to multiple aesthetics (`drv` to `y`, `fill`, and `color`) as well as set an aesthetic (`alpha = 0.5`) to make the density curves transparent.
```{r}
#| fig-asp: 0.33
#| fig-alt:
#| Density curves for highway mileage for cars with rear wheel,
#| front wheel, and 4-wheel drives plotted separately. The
#| distribution is bimodal and roughly symmetric for real and
#| 4 wheel drive cars and unimodal and right skewed for front
#| wheel drive cars.
library(ggridges)
ggplot(mpg, aes(x = hwy, y = drv, fill = drv, color = drv)) +
The best place to get a comprehensive overview of all of the geoms ggplot2 offers, as well as all functions in the package, is the reference page: <https://ggplot2.tidyverse.org/reference>.
In @sec-data-visualization you learned about faceting with `facet_wrap()`, which splits a plot into subplots that each display one subset of the data based on a categorical variable.
This is useful when you want to compare data across facets but it can be limiting when you want to visualize the relationship within each facet better.
Setting the `scales` argument in a faceting function to `"free"` will allow for different axis scales across both rows and columns, `"free_x"` will allow for different scales across rows, and `"free_y"` will allow for different scales across columns.
Consider a basic bar chart, drawn with `geom_bar()` or `geom_col()`.
The following chart displays the total number of diamonds in the `diamonds` dataset, grouped by `cut`.
The `diamonds` dataset is in the ggplot2 package and contains information on \~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}
#| fig-alt: >
#| Bar chart of number of each cut of diamond. There are roughly 1500
#| Fair, 5000 Good, 12000 Very Good, 14000 Premium, and 22000 Ideal cut
#| diamonds.
ggplot(diamonds, aes(x = cut)) +
geom_bar()
```
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:
- Bar charts, histograms, and frequency polygons bin your data and then plot bin counts, the number of points that fall in each bin.
- Smoothers fit a model to your data and then plot predictions from the model.
3. You might want to draw greater attention to the statistical transformation in your code.
For example, you might use `stat_summary()`, which summarizes the y values for each unique x value, to draw attention to the summary that you're computing:
```{r}
#| fig-alt: >
#| A plot with depth on the y-axis and cut on the x-axis (with levels
#| fair, good, very good, premium, and ideal) of diamonds. For each level
#| of cut, vertical lines extend from minimum to maximum depth for diamonds
#| in that cut category, and the median depth is indicated on the line
#| with a point.
ggplot(diamonds) +
stat_summary(
aes(x = cut, y = depth),
fun.min = min,
fun.max = max,
fun = median
)
```
ggplot2 provides more than 20 stats for you to use.
The stacking is performed automatically using 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` to a small value, or completely transparent by setting `fill = NA`.
There's one other type of adjustment that's not useful for bar charts, but 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
#| fig-alt: >
#| Scatterplot of highway fuel efficiency versus engine size of cars that
#| shows a negative association.
ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point()
```
The underlying 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 difficult to see the distribution of the data.
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.
```{r}
#| fig-alt: >
#| Jittered scatterplot of highway fuel efficiency versus engine size of cars.
#| The plot shows a negative association.
ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point(position = "jitter")
```
Adding randomness seems like a strange way to improve your plot, but while it makes your graph less accurate at small scales, it makes your graph *more* revealing at large scales.
Because this is such a useful operation, ggplot2 comes with a shorthand for `geom_point(position = "jitter")`: `geom_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`.
Create a visualization 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 positions act independently to determine the location of each point.
There are two other coordinate systems that are occasionally helpful.
This is very important if you're plotting spatial data with ggplot2.
We don't have the space to discuss maps in this book, but you can learn more in the [Maps chapter](https://ggplot2-book.org/maps.html) of *ggplot2: Elegant graphics for data analysis*.
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, a faceting scheme, and a theme.
To see how this works, consider how you could build a basic plot from scratch: you could start with a dataset and then transform it into the information that you want to display (with a stat).
Next, you could choose a geometric object to represent each observation in the transformed data.
You could then use the aesthetic properties of the geoms to represent variables in the data.
You would map the values of each variable to the levels of an aesthetic.
You'd then select a coordinate system to place the geoms into, using the location of the objects (which is itself an aesthetic property) to display the values of the x and y variables.
At this point, you would have a complete graph, but you could further adjust the positions of the geoms within the coordinate system (a position adjustment) or split the graph into subplots (faceting).
You could also extend the plot by adding one or more additional layers, where each additional layer uses a dataset, a geom, a set of mappings, a stat, and a position adjustment.
You could use this method to build *any* plot that you imagine.
In other words, you can use the code template that you've learned in this chapter to build hundreds of thousands of unique plots.
If you'd like to learn more about the theoretical underpinnings of ggplot2, you might enjoy reading "[The Layered Grammar of Graphics](https://vita.had.co.nz/papers/layered-grammar.pdf)", the scientific paper that describes the theory of ggplot2 in detail.
In this chapter you learned about the layered grammar of graphics starting with aesthetics and geometries to build a simple plot, facets for splitting the plot into subsets, statistics for understanding how geoms are calculated, position adjustments for controlling the fine details of position when geoms might otherwise overlap, and coordinate systems which allow you to fundamentally change what `x` and `y` mean.
Two very useful resources for getting an overview of the complete ggplot2 functionality are the ggplot2 cheatsheet (which you can find at <https://posit.co/resources/cheatsheets>) and the ggplot2 package website ([https://ggplot2.tidyverse.org](https://ggplot2.tidyverse.org/)).
An important lesson you should take from this chapter is that when you feel the need for a geom that is not provided by ggplot2, it's always a good idea to look into whether someone else has already solved your problem by creating a ggplot2 extension package that offers that geom.