This lab will continue to use the ggplot2 package:
library(ggplot2)
We will also continue using data from The College Scorecard:
colleges = read.csv("https://nfriedrichsen.github.io/data/Colleges2019.csv")
In the previous lab, we focused on the basic grammar used by
ggplot2: aesthetics, geometric elements, layers, and
facets. We also saw several ways to modify the appearance of a
graphic.
This lab will focus on gaining more precise control over those graphics.
There are several related ideas that will appear throughout this lab:
| Component | What does it control? |
|---|---|
| Mapping | Which variables control visual properties such as position, color, fill, or shape |
| Scale | How the values of a mapped variable are represented visually |
| Theme | Non-data components of the graph, such as text, backgrounds, grid lines, and legend placement |
| Guide | How the reader interprets a scale, usually through a legend or color bar |
ggplot(colleges, aes(x = Cost, y = Net_Tuition,
color = Adm_Rate)) +
geom_point() +
scale_color_viridis_c() +
theme_bw()
For example, if we use aes(color = Adm_Rate), we have
mapped Adm_Rate to the color aesthetic. A
function such as scale_color_viridis_c() controls the
scale used to translate values of Adm_Rate
into colors. The resulting color bar is a guide that
allows the reader to interpret those colors. Changing the
theme can modify the appearance of the graph without
changing this mapping.
Another important distinction throughout this lab is between
discrete and continuous variables.
Different scales are appropriate for different types of variables, and
recognizing this distinction will help you select appropriate scale
functions and understand some common ggplot2 error
messages.
The goal of this lab is not to memorize every
ggplot2 option. In particular, you do not need to memorize
theme component names, palette names, individual scale functions,
hexadecimal color codes, or all of the arguments available within the
element_*() functions. These are things you can look up
when you need them.
Instead, you should understand:
color and fill
aestheticsIn the next lab, we will focus more on answering this question: which graphical choices are effective for displaying different kinds of data and relationships? This lab will give us enough control over our graphics to make those choices.
\(~\)
In the previous lab, we saw that ggplot2 provides
several pre-built themes. For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
theme_bw()
Some commonly used themes include:
theme_bw()theme_linedraw()theme_light()theme_dark()theme_minimal()theme_classic()theme_void()These functions change the general appearance of a graphic. However,
we can also modify individual components of a theme using the
theme() function.
A useful distinction is that themes control the non-data components of a graphic. Changing a theme does not change which observations are displayed or how variables are mapped to aesthetics. Instead, themes control things such as text, backgrounds, grid lines, tick marks, and the placement of legends.
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
theme_minimal() +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold"),
panel.grid.minor = element_blank()
) +
labs(title = "College Cost and Net Tuition")
The theme() function contains many possible arguments
because there are many components of a graph that can be modified.
Fortunately, most of these components are modified using only a few
types of theme elements:
element_text()
element_line()
element_rect()
element_blank()
The appropriate function depends upon the type of graphical element being modified.
Text components are modified using element_text(). This
includes axis labels, axis tick labels, titles, and legend text.
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
labs(title = "College Cost and Net Tuition") +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold"),
axis.title = element_text(size = 16),
axis.text = element_text(size = 5), #smol
legend.text = element_text(angle = 45),
legend.title = element_text(angle = 45, vjust=-.5)
)
Some useful arguments within element_text() include:
element_text(
size = 12,
face = "bold",
color = "blue",
angle = 45, #rotate
hjust = 0.5, #horizontal adjustment (can be neg)
vjust = 0.5 #vertical adjustment (can be neg)
)
You generally will not use all of these at once. They simply provide different ways to control how text is displayed.
Theme components can also be modified together or individually. For example,
axis.text = element_text(size = 10)
modifies the text on both axes, while
axis.text.x = element_text(angle = 45)
modifies only the x-axis tick labels.
Line components are modified using element_line(). This
includes grid lines, axes, and tick marks. Useful arguments include
properties such as:
element_line(
color = "gray",
linewidth = 0.5,
linetype = "dashed"
)
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
theme_bw() +
theme(
panel.grid.major = element_line(linewidth = 0.5, color="blue", linetype = "dashed"),
panel.grid.minor = element_line(linewidth = 0.2),
axis.ticks = element_line(linewidth = 0.8)
)
You are most likely going to care about customizing colors, linewidths,
and line types.
Here are options for line types. You can use either the integer value
or the name like "dashed".
| Integer | String | Appearance |
|---|---|---|
0 |
"blank" |
No line is drawn |
1 |
"solid" |
Solid line (default) |
2 |
"dashed" |
Dashed line |
3 |
"dotted" |
Dotted line |
4 |
"dotdash" |
Alternating dots and dashes |
5 |
"longdash" |
Long dashed line |
6 |
"twodash" |
Alternating dash lengths |
Background regions and borders are generally modified using
element_rect().
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
theme(
panel.background = element_rect(fill = "white"),
panel.border = element_rect(color = "black", fill = NA),
legend.background = element_rect(fill = "white")
)
For rectangular elements, fill controls the interior
while color controls the border.
Sometimes the desired customization is simply to remove something.
element_blank() removes a theme component entirely.
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
theme(
panel.grid.minor = element_blank(),
axis.ticks = element_blank()
)
This is different from making an element white or transparent: the element is simply not drawn.
Many theme components follow a hierarchical naming structure. For example:
plot.title # Main plot title
plot.subtitle # Plot subtitle
axis.text # Axis tick labels on both axes
axis.text.x # X-axis tick labels
axis.text.y # Y-axis tick labels
axis.title # Axis titles on both axes
axis.title.x # X-axis title
axis.title.y # Y-axis title
axis.ticks # Tick marks on both axes
panel.grid # All grid lines
panel.grid.major # Major grid lines
panel.grid.minor # Minor grid lines
panel.background # Background of the plotting panel
panel.border # Border surrounding the plotting panel
legend.text # Labels for individual legend entries
legend.title # Legend title
legend.background # Background of the legend
The more general option changes all of the components beneath it, while a more specific option can be used when only one component should change.
You are not expected to memorize all of the available theme
components. Instead, you should understand the general
structure of theme() and recognize the four major element
functions. When you want to modify a particular part of a graph, you can
look up the appropriate theme component.
Begin with the following graph:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
labs(
title = "College Cost and Net Tuition",
subtitle = "Colleges in the 2019 College Scorecard",
x = "Sticker Cost",
y = "Net Tuition",
color = "College Type"
)
Apply a non-default theme of your choice and then experiment with the following theme customizations. Your final graph does not need to represent choices you would make for a polished visualization (it can be ugly); the goal is to practice modifying different theme elements. Only one final graph needs to be shown for this lab submission.
A. Move the legend to the bottom of the graph.
B. Make the title larger and bold.
C. Remove the minor grid lines.
D. Modify one line element using
element_line().
E. Modify one rectangular element using
element_rect().
F. Make one additional theme modification of your choice that has not already been requested.
Briefly identify the theme component and element_*()
function you used for Parts D, E, and F.
When a variable is mapped to the color or
fill aesthetic, ggplot2 automatically chooses
a set of colors to represent its values. However, we are not restricted
to these default colors.
A color palette is a collection or sequence of
colors designed to be used together. ggplot2 provides
access to several families of pre-built palettes, and we can also
construct our own.
For example, consider the default colors used to represent
Region:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Region)) +
geom_point()
There are several ways that we can replace these default colors.
One commonly used collection of palettes comes from
ColorBrewer. These palettes can be accessed in
ggplot2 using functions such as
scale_color_brewer().
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Region)) +
geom_point() +
scale_color_brewer(palette = "Set1") +
labs(title = "Set1")
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Region)) +
geom_point() +
scale_color_brewer(palette = "Dark2") +
labs(title = "Dark2")
The palette argument determines which collection of
colors is used.
Some (but not all) available ColorBrewer palettes include:
"Set1", "Set2", "Set3",
"Dark2", "Paired", "Pastel1",
"Pastel2", "Accent"
These are examples of qualitative palettes. Qualitative palettes use different colors to distinguish categories without implying that one category is larger or smaller than another.
We can see the available “qualitative” (designed for categorical) ColorBrewer palettes using:
RColorBrewer::display.brewer.all(type = "qual")
ColorBrewer also contains palettes designed for quantitative values. For example,
RColorBrewer::display.brewer.all(type = "seq")
displays sequential palettes, while
RColorBrewer::display.brewer.all(type = "div")
displays diverging palettes.
Sequential palettes progress from one end of a color scale to another. Diverging palettes have two different ends with a contrasting color near the center.
For now, the important point is simply that different types of palettes exist. We will discuss more about choosing an effective palette for a particular purpose in the next lab.
Modify the graph we have been looking at and explore the different qualitative ColorBrewer palette options. Make a list of your favorite ones so far. Are there palettes that make the different regions easier or more difficult to distinguish?
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Region)) +
geom_point()
\(~\)
Another commonly used family of color palettes that deserves special attention is viridis.
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_viridis_c(option = "viridis") +
labs(title = "Viridis")
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_viridis_c(option = "plasma") +
labs(title = "Plasma")
Several palettes are available within this family:
"viridis", "magma", "inferno",
"plasma", "cividis", "mako",
"rocket", "turbo"
The palette is selected using the option argument:
scale_color_viridis_c(option = "magma")
The direction of the palette can also be reversed using
direction = -1:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_viridis_c() +
labs(title = "Default Direction")
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_viridis_c(direction = -1) +
labs(title = "Reversed Direction")
For the plot below, experiment with the viridis color palettes and
see which ones work best for this graph. Also try out if
direction=-1 gives you a better graph.
Salary10yr_median is the median salary of graduates 10
years after graduating. What does this plot tell you about the
relationship between these 3 variables?
ggplot(data = colleges, aes(x = Avg_Fac_Salary, color = Debt_median, y=Salary10yr_median)) + geom_point()
\(~\)
We can also construct a continuous color palette directly.
The scale_color_gradient() function creates a gradient
between two colors:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_gradient(
low = "purple",
high = "yellow"
)
Here, smaller values of Adm_Rate are represented closer
to purple and larger values closer to yellow.
A gradient can also contain a specified middle color using
scale_color_gradient2:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_gradient2(
low = "blue",
mid = "white",
high = "red",
midpoint = 0.5
)
The midpoint argument determines which numerical value
receives the middle color. This creates a diverging
gradient centered at the specified value.
\(~\)
For categorical variables, we can also specify the exact color assigned to each category.
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
scale_color_manual(
values = c(
"Public" = "purple",
"Private" = "orange"
)
)
Using a named vector makes the correspondence between categories and colors explicit:
"Public" = "purple"
"Private" = "orange"
rather than relying upon the order in which the categories happen to appear.
Colors can be specified using many standard color names. You can see
the named colors recognized by R using:
# run this in your own console
colors()
R also understands hexadecimal color codes, which can be
used when there is a particular color shade/hue/brightness combination
you want.
"#440154"
"#21918C"
"#FDE725"
\(~\)
Throughout the previous section, we used several functions to change how colors were displayed:
scale_color_brewer()
scale_color_viridis_c()
scale_color_gradient()
scale_color_gradient2()
scale_color_manual()
Although we introduced these functions as ways to choose color palettes, they are all examples of scales.
A scale controls how the values of a variable are translated into the
visual properties of a graph. For example, suppose we map
Adm_Rate to color:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point()
The aesthetic mapping color = Adm_Rate tells
ggplot2 which variable should control
color. The color scale determines how the values of that
variable are represented by colors.
For example, both of the following graphs have exactly the same aesthetic mappings:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_gradient(low = "white", high = "red")
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_viridis_c(option = "magma")
The data and mappings have not changed. Only the
scale used to display the color aesthetic
has changed.
\(~\)
Scale functions generally follow a common naming structure:
scale_[aesthetic]_[type]()
The first part tells ggplot2 which aesthetic is being
controlled. For example:
scale_color_...
scale_fill_...
scale_x_...
scale_y_...
The remainder of the function name describes the type of scale being used.
For example:
scale_color_gradient()
scale_color_manual()
scale_fill_gradient()
scale_fill_manual()
scale_x_continuous()
scale_y_continuous()
This naming system means that once you understand how scales work, you do not need to memorize every possible scale function individually.
\(~\)
An important distinction is whether the variable mapped to an aesthetic is discrete or continuous.
A discrete variable consists of separate categories.
For example, Type and Region are discrete
variables.
A continuous variable is numerical and can take
values along a numerical scale. For example, Adm_Rate,
Cost, and Net_Tuition are continuous
variables.
For the math savvy: Please don’t yell at me about discrete also having different meaning for quantitative variables. I didn’t invent the system!
Different scale functions are designed for these different types of variables.
For example, the following graph maps the categorical variable
Type to color:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
scale_color_brewer(palette = "Dark2")
In contrast, the following graph maps the quantitative variable
Adm_Rate to color:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_viridis_c()
Some scale functions make this distinction explicit in their names. In the viridis family, for example:
scale_color_viridis_d() # discrete
scale_color_viridis_c() # continuous
The _d version is used when colors represent categories,
while the _c version is used when colors represent values
along a continuous numerical scale.
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
scale_color_viridis_d()
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
scale_color_viridis_c()
Other scale functions are designed specifically for one type of
variable even if _d or _c does not appear in
the function name. For example, scale_color_gradient()
represents continuous values using a gradient, while
scale_color_manual() allows us to assign values to
individual categories.
Consider the following code:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
scale_color_viridis_c()
A. Run the code. Read the error message and explain
why scale_color_viridis_c() cannot be used here.
B. Modify the scale function so that the graph works correctly.
C. The ColorBrewer palettes we encountered earlier
can also be used with discrete or continuous variables. Investigate the
difference between scale_color_brewer() and
scale_color_distiller(). Which function would be
appropriate for Type? Recreate the graph using that function and a
ColorBrewer palette of your choice.
The color aesthetic is not synonymous with every color
that appears on a graph.
In ggplot2, color generally controls the
color of points and lines or the outline of a shape,
while fill controls the color of its
interior.
For example:
ggplot(data = colleges,
mapping = aes(x = Type, fill = Type)) +
geom_bar() +
labs(title = "Fill")
ggplot(data = colleges,
mapping = aes(x = Type, color = Type)) +
geom_bar() +
labs(title = "Color")
Because color and fill are different
aesthetics, they also have different scale functions.
For example:
scale_color_brewer()
scale_fill_brewer()
scale_color_viridis_c()
scale_fill_viridis_c()
scale_color_manual()
scale_fill_manual()
The scale function needs to correspond to the aesthetic that was actually mapped.
For example:
ggplot(data = colleges,
mapping = aes(x = Type, fill = Type)) +
geom_bar() +
scale_fill_brewer(palette = "Set2")
Here Type is mapped to fill, so we use
scale_fill_brewer() rather than
scale_color_brewer().
Scales are not limited to color. The x- and y-axes of a graph are also controlled by scales.
For example:
ggplot(data = colleges,
mapping = aes(x = Enrollment, y = Net_Tuition)) +
geom_point()
Since Enrollment is quantitative, its x-axis is
controlled by a continuous position scale. We can explicitly modify this
scale using:
scale_x_continuous()
Similarly, a continuous variable on the y-axis can be modified using:
scale_y_continuous()
One useful application is changing the transformation used to display an axis.
For example, college enrollment is highly skewed because a relatively small number of institutions have extremely large enrollments:
ggplot(data = colleges,
mapping = aes(x = Enrollment, y = Net_Tuition)) +
geom_point() +
labs(title = "Original Scale")
ggplot(data = colleges,
mapping = aes(x = Enrollment, y = Net_Tuition)) +
geom_point() +
scale_x_continuous(transform = "log2") +
labs(title = "Log2 Scale")
Both graphs contain the same observations. The second graph changes the scale used to display their positions on the x-axis.
Other transformations are available. For example:
scale_x_continuous(transform = "log10")
scale_x_continuous(transform = "sqrt")
scale_x_continuous(transform = "reverse")
Similar transformations can be applied to the y-axis using
scale_y_continuous().
A transformation changes how values are positioned along an axis; it does not remove or alter the original observations in the data.
Transforming a scale is not only useful for changing the appearance of a graph. Sometimes a transformation can reveal a relationship between variables that is difficult to see on their original scales. This can be particularly useful when we are interested in fitting a linear relationship between variables.
Consider the following graph:
ggplot(data = colleges,
mapping = aes(x = Enrollment, y = Cost, color = Type)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE)
The lines show the fitted linear relationship between
Enrollment and Cost separately for public and
private colleges.
A. Examine the graph and the two fitted lines. Does
a linear relationship appear to describe the relationship between
Enrollment and Cost particularly well on the
original scale? Explain briefly.
B. Modify the x-axis using
scale_x_continuous(transform = "log2")
How does transforming Enrollment change the appearance
of the relationship?
C. Based on the transformed graph, does either
college type seem to have a roughly linear relationship with
log2(Enrollment)? Describe what you see.
When an aesthetic represents a variable, the reader needs some way to determine what the different colors, shapes, or other visual properties mean. These references are called guides.
Guides are closely connected to the aesthetic mappings and scales we
have already discussed. The mapping in aes() determines
which variable controls an aesthetic, the scale
determines how the values are displayed, and the guide
explains that scale to the reader.
For categorical color mappings, the guide will usually appear as a legend:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point()
For continuous color mappings, the guide commonly appears as a color bar:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point()
When modifying a guide, we generally refer to it using the
name of the aesthetic that created it. In other words,
look back at the argument used inside aes().
For example, if we map
aes(color = Adm_Rate)
then the corresponding guide is associated with color.
We can change its title using:
labs(color = "Admissions Rate")
Similarly, a guide created by fill = Type would be
modified using fill, while a guide created by
shape = Type would be modified using
shape.
For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Adm_Rate)) +
geom_point() +
labs(
x = "Sticker Cost",
y = "Net Tuition",
color = "Admissions Rate"
)
Notice that we use color = "Admissions Rate" rather than
Adm_Rate = "Admissions Rate". Here, color
refers to the aesthetic, while
"Admissions Rate" is the new title we want displayed for
its guide.
A graph can have multiple guides when multiple aesthetics are mapped to variables. For example:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition,
color = Region, shape = Type)) +
geom_point() + scale_color_brewer(palette="Accent")
Here, color represents Region while
shape represents Type. ggplot2
therefore provides a guide explaining each mapping.
Each guide can be modified using the corresponding aesthetic name:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition,
color = Region, shape = Type)) +
geom_point() + scale_color_brewer(palette="Accent") +
labs(
color = "College Region",
shape = "College Type"
)
This produces two separate guides, and with many categories they can take up quite a bit of space. There are additional options for controlling the size and arrangement of guides, but we will not worry about those details here. The reality is that we should probably find a different way to visualize this many categories.
As we saw earlier, the position of the guides can be changed using
theme():
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
theme(legend.position = "bottom")
Guides can also be removed when they are not needed:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition, color = Type)) +
geom_point() +
theme(legend.position = "none")
Removing a guide does not remove the aesthetic mapping. It only removes the reference that explains the mapping to the reader.
In the previous lab, we saw that data and aesthetic mappings placed
in ggplot() are inherited by subsequent layers:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition)) +
geom_point() +
geom_smooth()
However, individual layers can supply their own data, aesthetic mappings, and appearance options.
For example, we can display every college in the background but add a second layer containing only Iowa colleges:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition)) +
geom_point(alpha = 0.2) +
geom_point(
data = subset(colleges, State == "IA"),
color = "red",
size = 3
)
Both layers inherit x = Cost and
y = Net_Tuition from ggplot(). However, the
second layer replaces the inherited data with the subset of Iowa
colleges.
The second layer could also add its own aesthetic mapping:
ggplot(data = colleges,
mapping = aes(x = Cost, y = Net_Tuition)) +
geom_point(alpha = 0.2) +
geom_point(
data = subset(colleges, State == "IA"),
mapping = aes(color = Type),
size = 3
)
Now only the Iowa colleges are colored according to
Type.
Notice that the individual layer has three things we can control separately:
data — which observations are displayed by the
layermapping — which variables control visual
propertiessize,
alpha, or a particular colorThe order of the layers also matters. Layers added later are drawn over layers added earlier. This is why the highlighted Iowa colleges appear on top of the faint background points.
Modify the previous graph as follows:
Type to shape
and adjust alpha so shapes are visible but not dominating
the Iowa data.Type color mapping for the Iowa colleges.Your graph should look roughly like the following (different colors and shapes are OK):
\(~\)