Design with a discerning, editing eye
**Download the R syntax and data used in this post **
TL;DR: This post is about leveraging ggplot2 theme elements
to customize the non-data components of your charts.
A common question I get from clients and audience members when I give talks or presentations on data + design is:
How can I make my designs look as polished as yours?
Here's the thing. There are many resources out there to help you learn how to create charts and graphs using a wide variety of tools and software programs. But so many of them miss the mark on one critical aspect of visual design:
Knowing how and when to edit.
A keen editing eye and knowledge of a few fundamental design principles can elevate your designs to new heights, allowing for effective communication and aesthetic appeal.
One of the best ways to apply an editing eye is to customize the non-data components of your visualizations. In this post, I will show you how to apply this skill by creating a custom theme using R's ggplot2
package.
Setting the Stage
For this post, we will use data from the World Bank's World Development Indicators database on access to electricity (i.e., the percentage of the population with access to electricity) and the proportion of seats held by women in national parliaments in 16 West African countries from 2015 to 2020. Rather than overlay the data for each of the 16 countries on one chart, we will use the facet_wrap()
function from ggplot2
to create a 4 x 4 panel of line charts.
First, load ggplot2
and import the access to electricity
data:
### load ggplot2
library(ggplot2)
### Import data
electricty_access <- read.csv("sm_electricty_access.csv")
## order country names
# greatest to least access
electricty_access$country <- factor(
electricty_access$country,
levels = c("Cabo Verde","Ghana","Senegal",
"Cote d'Ivoire","The Gambia","Nigeria",
"Togo","Mali","Mauritania","Guinea","Benin",
"Guinea-Bissau","Liberia","Sierra Leone",
"Niger","Burkina Faso"),
labels = c("Cabo Verde","Ghana","Senegal",
"Cote d'Ivoire","The Gambia","Nigeria",
"Togo","Mali","Mauritania","Guinea","Benin",
"Guinea-Bissau","Liberia","Sierra Leone",
"Niger","Burkina Faso")
)
Take a peek at the data:
The Basic Plot
Next, build a basic small multiples line chart using the data. One way you could do that is by using the geom_line()
and facet_wrap()
functions. Note that scale_y_continuous()
and scale_x_continuous()
are used to customize the labels displayed along the vertical and horizontal axes, respectively. Include a plot title that reads Access to electricity has changed over time across West African nations using the labs()
function.
## basic plot
ggplot(electricty_access) +
geom_line(
aes(x = year, y = percent_access,
group = country), colour = "#3e3e3e",
linewidth = 1) +
scale_y_continuous(
limits = c(0, 1),
breaks = seq(0, 1, by = 0.25),
labels = paste0(seq(0, 1, by = 0.25) * 100, "%"),
expand = expansion(add = 0.001)) +
scale_x_continuous(
limits = c(2015, 2020),
breaks = seq(2015, 2020, 1),
labels = paste0("'", substring(c(2015:2020), 3, 4)),
expand = expansion(add = 0.001)) +
facet_wrap(facets = ~ country,
strip.position = "top",
scales = "free") +
labs(title = "Access to electricity has changed over time across West African nations.")
The basic plot has a lot going on:
- The overall title does not stand out. (You almost miss it!)
- Each chart's grey background and dark axis tick marks are distracting.
- Each chart has a grey label containing each country's name.
So, there is a fair amount we may want to edit, including:
- Bolding, increasing the size of, and left aligning the plot title.
- Muting or removing the tick marks.
- Toning down the plot background (the grey fill color coupled with the white gridlines makes it hard to focus on the data).
- Remove the axis titles.
- Increasing the size of each country's name and removing the grey label background.
But here is the best part: you don't have to start from scratch!
Custom and Complete (Built-in) Themes
Like most packages, ggplot2
contains data and a bunch of functions. One set of functions contained within the package are complete themes
. These built-in functions provide a simple method to customize the appearance of your visualizations. Here are a few of my favorite themes:
theme_gray()
: the classic ggplot2 theme with a grey plot background and white gridlines.theme_bw()
: a variation of theme_grey() that uses a white plot background and thin grey grid lines.theme_minimal()
: a minimalist theme without background annotations and few lines.theme_void()
: an empty theme.
To learn more about ggplot themes, visit the complete themes section of the online ggplot2 book.
Applying Built-in Themes
To apply one of the built-in themes to a plot, add it to your ggplot object using the +
operator. Here's an example of how to add the theme_minimal()
complete theme to our basic plot:
#### basic plot with theme_minimal
ggplot(electricty_access) +
geom_line(
aes(x = year, y = percent_access,
group = country), colour = "#3e3e3e",
linewidth = 1) +
scale_y_continuous(
limits = c(0, 1),
breaks = seq(0, 1, by = 0.25),
labels = paste0(seq(0, 1, by = 0.25) * 100, "%"),
expand = expansion(add = 0.001)) +
scale_x_continuous(
limits = c(2015, 2020),
breaks = seq(2015, 2020, 1),
labels = paste0("'", substring(c(2015:2020), 3, 4)),
expand = expansion(add = 0.001)) +
facet_wrap(facets = ~ country,
strip.position = "top",
scales = "free") +
labs(title = "Access to electricity has changed over time across West African nations.") +
theme_minimal()
Whew. Much better, right? We can actually see the data.
Let's try another built-in theme.
Here's what our basic plot looks like after applying theme_void()
:
### basic plot with theme_void
ggplot(electricty_access) +
geom_line(
aes(x = year, y = percent_access,
group = country), colour = "#3e3e3e",
linewidth = 1) +
scale_y_continuous(
limits = c(0, 1),
breaks = seq(0, 1, by = 0.25),
labels = paste0(seq(0, 1, by = 0.25) * 100, "%"),
expand = expansion(add = 0.001)) +
scale_x_continuous(
limits = c(2015, 2020),
breaks = seq(2015, 2020, 1),
labels = paste0("'", substring(c(2015:2020), 3, 4)),
expand = expansion(add = 0.001)) +
facet_wrap(facets = ~ country,
strip.position = "top",
scales = "free") +
labs(title = "Access to electricity has changed over time across West African nations.") +
theme_void()
I adore theme_void()
. Why? I like to build my designs layer by layer. As I add to the design, I carefully consider the role each element plays in the overall look, feel, and behavior of the design. Does it add meaning or value? Will it overwhelm the audience? Will it obscure important information?
Adding Your Personal Touch
Once you decide which built-in theme you would like to use for your design, you can add a secondary theme layer to customize specific elements of the base theme. Let's say you were unhappy with theme_void()
's handling of gridlines and wanted major gridlines for both the x and y axis to be displayed in a muted grey color. You could add the following to your base plot:
theme_void() +
theme(
# add both x and y-axis major gridlines in a muted grey color
panel.grid.major = element_line(color = "#bfbfbf", linewidth = 0.08)
)
to produce this plot:
See what a difference a few lines can make?
Turn Your Theme into a Function
Once you have carefully crafted a fully customized theme, you can apply that theme to virtually any plot you create. The easiest way to do that is to transform your theme into a function.
Here's an example of a personalized theme that has been converted into a function called ama_theme_spark()
:
### Turning a customized theme into a function
ama_theme_spark <- function(){
## apply the custom "void" theme
theme_void() +
## further customize the theme
theme(
# set plot title spacing, font, size, face, and color
plot.title = element_text(
margin = margin(t = 0, r = 0, b = 0.5, l = 0, unit = "cm"),
color = "#3e3e3e",face = "bold", size = 14, family = "Helvetica Neue",
hjust = 0, vjust = 1),
# left-align plot title
plot.title.position = "plot",
# set plot caption spacing, font, size, face, and color
plot.caption = element_text(
margin = margin(t = 0.25, r = 0, b = 0, l = 0, unit = "cm"),
color = "#818181", face = "plain", size = 7, family = "Helvetica Neue",
hjust = 0, vjust = -1),
# left-align plot caption
plot.caption.position = "plot",
# set x and y axis text spacing, font, size, face, and color
axis.text = element_text(
color = "#595959", face = "plain", size = 7,
family = "Helvetica Neue" , hjust = 0.5),
# add some spacing between axis text labels and axis line
axis.ticks.length = unit(0.15, "cm"),
# add both x and y-axis major gridlines in a muted grey color panel.grid.major = element_line(color = "#bfbfbf", linewidth = 0.08),
# customize facet wrap strip text labels (margin, spacing, text color, face, and family)
strip.text.x = element_text(
margin = margin(
t = 0, r = 0, b = 0.3, l = 0, unit = "cm"),
hjust = 0.5, angle = 0, color = "#3e3e3e",
face = "bold", size = 10, family = "Helvetica Neue"),
# set x axis panel spacing
panel.spacing.x = unit(1.5, "cm"),
# set y axis panel spacing
panel.spacing.y = unit(0.55, "cm"),
# set plot margins
plot.margin = margin(
t = 0.5, r = 1.0, b = 0.5, l = 0.25, "cm"
)
)
}
Notice how the first line of the function is theme_void()
? This signals to R that we are using theme_void()
as a starting point for our custom theme.
Once the function has been created, we can then go on to apply it to our base plot:
ggplot(electricty_access) +
geom_line(
aes(x = year, y = percent_access,
group = country), colour = "#3e3e3e",
linewidth = 1) +
scale_y_continuous(
limits = c(0, 1),
breaks = seq(0, 1, by = 0.25),
labels = paste0(seq(0, 1, by = 0.25) * 100, "%"),
expand = expansion(add = 0.001)) +
scale_x_continuous(
limits = c(2015, 2020),
breaks = seq(2015, 2021, 1),
labels = paste0("'", substring(c(2015:2020, ""), 3, 4)),
expand = expansion(add = 0.001)) +
facet_wrap(facets = ~ country,
strip.position = "top",
scales = "free") +
labs(title = "Access to electricity has changed over time across West African nations.") +
ama_theme_spark()
And beyond!
Here is another 4 x 4 panel of line charts using data on the proportion of seats held by women in national parliaments in 16 West African countries from 2015 to 2020 and our customized ggplot2
theme, ama_theme_spark()
:
I hope this post has inspired you to create and use custom themes when designing visualizations in R. Full disclosure: the theme()
function has many arguments you can specify. To learn more about the dozens of unique theme elements you can customize using the theme()
function, visit the theme elements section of the online ggplot2 book. Dig in and create some unique themes!
And be sure to check out some of my other posts if you want to learn other ways you can develop a sharper editing eye:
Do you need one-on-one support developing custom visuals? Feel like you could use a hand with your DataViz? Contact me if you're ready to learn how to create clear and compelling charts or design customized systems that produce user-friendly and consistent visuals. Together, we can build a plan to bring your data to life.