Design with a discerning, editing eye

**Download the R syntax and data used in this post **

GIF of small multiples line chart showing the percentage of each country's population with access to electricity between 2015 and 2020. Slide 1: shows the default (or basic) plot produced using R's ggplot. Slide 2: shows the plot produced using ggplot2's theme_minimal() built-in theme. Slide 3: shows the plot produced using ggplot2's theme_void() built-in theme. Slide 4: shows the plot produced using ggplot2's theme_void() built-in theme along with some additional modifactions.

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:

Image of the first 18 rows of the data set. The data have 96 rows and four columns. The columns are code (three-letter abbreviation for country), country (country name), year (year), and percent_access (the percentage of the population with access to electricity between 2015 and 2020.

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.")
Default small multiples line chart showing the percentage of each country's population with access to electricity between 2015 and 2020.

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()
Small multiples line chart with the theme_minimal() complete theme applied showing the percentage of each country's population with access to electricity between 2015 and 2020. Gridlines are a muted grey color, each country's plot no longer has a grey background, and country labels no longer have a grey background. Both x (i.e., year) and y-axis (percent_access) titles are still present.

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()
Small multiples line chart with the theme_void() complete theme applied showing the percentage of each country's population with access to electricity between 2015 and 2020. The plots do not have gridlines, data tick labels or background fill color, nor do country labels have a grey background. Finally, no axis titles are present.

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:

Small multiples line chart with the theme_void() complete theme applied showing the percentage of each country's population with access to electricity between 2015 and 2020. Each chart has muted grey gridlines, but no tick labels or background fill color, and country labels do not have a grey background. Finally, no axis titles are present.

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()
Small multiples line chart with a fully customized theme applied showing the percentage of each country's population with access to electricity between 2015 and 2020. Each chart has muted grey gridlines with x-axis tick labels showing abbreviated years (e.g., '15 for 2015) and y-axis tick labels ranging from 0% to 100%. Country labels have a white background, and country names are in a bold font. Finally, no axis titles are present.

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():

Small multiples line chart with a fully customized theme showing the proportion of seats held by women in national parliaments in 16 West African countries between 2015 and 2020. Each chart has muted grey gridlines with x-axis tick labels showing abbreviated years (e.g., '15 for 2015) and y-axis tick labels ranging from 0% to 100%. Country labels have a white background, and country names are in a bold font. Finally, no axis titles are present.

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.

Previous
Previous

Renaming Columns in Python

Next
Next

Order Your Data with Intention – ggplot edition