The world’s leading publication for data science, AI, and ML professionals.

Designing Custom ggplot2 Themes

Give your graphs an attitude

Original art by the author
Original art by the author

The R versus Python debate is a favorite among data scientists. When it comes to Data Visualization, however, yours truly has a very clear preference: ggplot2 in R all the way.

Ease of data handling and formatting aside, ggplot2 shines in its customizability. The defaults look great, albeit very distinct, but with a bit of planning you can totally transform visualizations to fit any imaginable aesthetic. In this article I’ll walk through how to create a custom ggplot2 theme that can be applied to your graphs.

First things first

Open up a new R script that will contain your customizations. You’ll run this script before building any visualizations in order to make your settings available. You can package it up into a library, too, though that’s not strictly necessary.

Make sure to load up the necessary libraries. There’s nothing surprising here, just make sure ggplot and scales are loaded in before you do anything else.

library("Ggplot2")
library("scales")

Fonts

New font, new you. Right? There are several options for using a custom font in your graphs.

The first option involves using the fonts that are already on your machine. To access them, load the extrafont library and import all fonts to R. Warning: This is slow. Once you’ve run font_import(), comment it out and leave it alone. If you’re not sure what fonts are possible, use system_fonts() to see a list.

library("extrafont")
font_import() # only run this once then comment out. It's slow!
extrafont::loadfonts()
library("systemfonts")
system_fonts() # shows you available fonts

The second option is to add in a Google font. The showtext library will let you import a font from the Google font page using the font_add_google() function. In this example, I’m importing the Reenie Beanie font for a fun handwriting effect.

library("showtext")
## Loading Google fonts (https://fonts.google.com/)
graphFont = "Reenie Beanie"
font_add_google(graphFont)

Themes

Under normal circumstances you might use the theme() argument to make small changes to the default settings. Because ggplot2 is layered, adding +theme() to the end of a visualization will override any preceding settings.

We can take advantage of this behavior by defining a bespoke theme that can be added at the end of any visualization. This means you could even go back to old work and apply your new theme to achieve a consistent portfolio. In this example, I’ve defined a straightforward theme that makes all the text black, specifies various background fills, and sets the font. Running theme without the parentheses in R will show you all possible theme options.

theme_custom = function(){
  theme(
    panel.background = element_blank(),
    panel.grid = element_blank(),
    axis.ticks = element_blank(),
    axis.text = element_text(color = "black"),
    legend.box.background = element_blank(),
    legend.key = element_rect(fill = NA),
    text = element_text(graphFont),
    plot.title = element_text(size = 16)
  )
}

Color Scales

Setting up color pre-planned color scales can be a huge time saver—say goodbye to guessing which shade of magenta you used last time. I like to use the coolors app to create color palettes, and then create a vector of hex codes.

Remember that you’ll need to set a scale for both scale_color_manual() and scale_fill_manual() to account for the different geoms.

pal_custom = c("#9e0a0a", "#115035", "#ff9e97", "#f6cb4c")
### Scales 
custom_color_manual = function(){
  scale_color_manual(values = pal_custom)
}
custom_fill_manual = function(){
  scale_fill_manual(values = pal_custom)

Custom geoms

If you would like specific behavior for certain types of graphs, customizing the geoms is the way to go. The update_geom_defaults() function does the trick. In these examples, I’ve requested that points be at least size 3, bars have white outlines, and lines are size 1.

update_geom_defaults("point", list(size = 3))
update_geom_defaults("bar", list(color = "white"))
update_geom_defaults("line", list(size = 1))

Layering Themes

As if one theme wasn’t enough, you can actually layer themes. If, for example, you are only making small changes to an existing ggplot theme, you can run the original first, and add your changes afterward.

In this example, I used the xkcd theme by Emilio Torres-Manzanera to achieve a hand-drawn look on the axes, and then applied my custom theme at the end.

ggplot(data=iris, aes(x=Sepal.Width)) + 
  geom_histogram(binwidth=0.1, aes(fill=Species)) + 
  custom_fill_manual() + 
  xlab("X-axis title") +  
  ylab("Y-axis title") + 
  ggtitle("Main chart title") + 
  theme_xkcd() +
  xkcdaxis(range(iris$Sepal.Width),range(c(0, 30)))  + 
  theme_custom()

The Before and After

This particular theme might not be your cup of tea, but it’s certainly different from the default look:

The default ggplot2 theme (left) shown in comparison to a customized theme (right)
The default ggplot2 theme (left) shown in comparison to a customized theme (right)

Testing

Establishing a custom aesthetic for your visualizations will take some iteration and testing. I recommend building a few test visuals that you can run when you update your theme—this will let you preview the effects on different chart types. Having a handful of test figures will also allow you to conduct accessibility checks and ensure that your theme is user-friendly. I use the Sim Daltonism app to evaluate color palettes and the Accessibility Insights extension to check web elements.

A knitr (.Rnw) or R Markdown (.Rmd) script works nicely for testing because it can be easily recompiled to produce a sharable document. Something as simple as this will do the trick:

documentclass[a4paper]{article}
begin{document}
<<projections, include=FALSE, cache=FALSE>>==
source("branding.R")
opts_chunk$set(dev='cairo_pdf')
@
<<renderimage, echo = FALSE, fig.height = 3, fig.width = 5, fig.align='center'>>==
ggplot(data=iris, aes(x=Sepal.Width)) + 
  geom_histogram(binwidth=0.1, aes(fill=Species)) + 
  custom_fill_manual() + 
  xlab("X-axis title") +  
  ylab("Y-axis title") + 
  ggtitle("Main chart title") + 
  theme_xkcd() +
  xkcdaxis(range(iris$Sepal.Width),range(c(0, 30)))  + 
  theme_custom()
@
end{document}

And that’s it! I hope this gives you a few ideas on how you can create a custom theme for your own ggplot2 work. Start small, see what works, and build from there. Your graphs will be turning heads in no time.

Thinking about accessibility in data visualization? Great! Here are some ways to get started:

An Incomplete Guide to Accessible Data Visualization


Related Articles