Chapter 4 Customising charts

Until now, we have been using pre-built themes like theme_classic() or theme_bw() to make our charts more presentable. But what if we want to be able to customise the appearance of all components of a chart ourselves?

In this chapter, we will explore how to tweak everything from your chart title, axis labels, legends, dimensions and more.

4.1 Labels

Labels are a key ingredient in rendering a chart understandable. They are added with the labs() function. Available options are given below:

Option Usage
title main title
subtitle subtitle
caption caption (bottom right by default)
x horizontal axis
y vertical axis
color colour legend title
fill fill legend title
size size legend title
linetype linetype legend title
shape shape legend title
alpha transparency legend title

For example:

Code
ggplot(driving_bar_scaled, aes(x = Financial_Year, y = Pass_rate, fill = Gender)) +
  geom_col(position = "dodge") +
  scale_fill_brewer() +
  scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  theme_classic() +
  labs(title = "Driving test pass rate by gender",
       subtitle = "Data from 2012 to 2022",
       caption = "Source: Department for Transport",
       y = "% of passes",
       x = "Year")

4.1.1 Exercise

15:00

  1. Using the stacked bar chart you made in Exercise 2.2.3, add a title, x and y axis labels, and any other component you would like. For example, caption, subtitle.

Solution

Code
ggplot(transport_bar_data, aes(x = year, y = usage, fill = transport)) +
  geom_col(position = "stack") +
  geom_text(aes(label = transport), position = "stack", vjust = 1.8, size = 3) +
  # Let's add our labs
  labs(title = "Average transport modes usage over 3 years",
       caption = "Source: Department for Transport",
       x = "Year",
       y = "Usage") +
  #add our colours and theme
  scale_fill_brewer(palette = "PuBuGn")+
  theme_classic()

4.2 Building custom themes

But what if you would like to decide the placement and size of your labels?

We can do this by starting to build our own theme.

To create our own theme in ggplot2, use the theme() function and add it to the code simply as another layer. Some of the common components we might want to change are text, legend, background, plot outlines and more.

  • element_text() is used to change anything to do with text, such as axis text or title (e.g. element_text(size = 10))
  • element_line() is used to change anything to do with lines, such as grid lines (e.g. element_line(colour = "grey"))
  • element_rect() is used to change anything to do with borders and backgrounds (e.g. element_rect(fill = "white")
  • element_blank() is used to remove a component of a chart, such as a legend (e.g. element_blank())

There are many more elements you can change, and you can find them all in the ?theme().

To change Theme Argument Function
Overall text size text e.g. element_text(size = 10)
X axis label axis.title.x element_text()
Y axis label axis.title.y element_text()
X axis text axis.text.x element_text()
Y axis text axis..text.y element_text()
Title of the plot plot.title element_text()
Caption plot.caption element_text()
Change legend position legend.position e.g. legend.position = "bottom"
Legend title legend.title element_text()
Legend labels legend.text element_text()
Legend background legend.background e.g. element_rect(fill = "lightblue")
Plot background panel.background e.g. element_rect(fill = "white")
Plot margin size plot.margin margin(2, 4, 2, 2, "cm")
Add major grid lines panel.grid.major element_line(linetype = "dashed")
Change minor grid lines colour panel.grid.minor element_line(colour = "grey")

4.3 Changing text elements

The very first thing to note from this point onwards is that to change any of the elements, it will require making your own theme. To initialise creating our own them, use the theme() function.

Inside the theme() function, it is as simple as stating which text element you would like to change, equating to the element_text() function, and then using any parameters inside the element_text() function you would like to alter.

Code
ggplot(driving_bar_scaled, aes(x = Financial_Year, y = Pass_rate, fill = Gender)) +
  geom_col(position = "dodge") +
  scale_fill_brewer() +
  scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  labs(title = "Driving test pass rate by gender",
       y = "% of passes",
       x = "Year") +
  #create theme 
  theme(
        # Change size of text/numbers on the axis
        axis.text.x = element_text(size = 10, angle = 45),
        
        # Change size of text/numbers on the axis
        axis.text.y = element_text(size = 10),
        
        # Change size of title on the axis
        axis.title.y = element_text(size = 12),
        
        # Change size of title on the axis
        axis.title.x = element_text(size = 12),
        
        # Change formatting of chart title, such as "bold" or where its placed 
        plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5)
        )

4.3.1 Exercise

15:00

You will be using the chart created in the previous exercise, focusing on customising your own theme with theme(). Replace the in built themes with your own theme:

  1. Change the text aesthetics, such as size, formatting, angle or placement of your:
  • x axis text
  • x axis title
  • y axis text
  • y axis title
  • chart title
  1. Are there any other text elements you can change? Such as the legend? Use the table above to change it.

Solution

Code
ggplot(transport_bar_data, aes(x = year, y = usage, fill = transport)) +
  geom_col(position = "stack") +
  geom_text(aes(label = transport), position = "stack", vjust = 1.8, size = 3) +
  labs(title = "Average transport modes usage over 3 years",
       x = "Year",
       y = "Usage") +
  scale_fill_brewer(palette = "PuBuGn") +
  #create theme 
  theme(
    axis.text.x = element_text(size = 10),
    axis.text.y = element_text(size = 10),
    axis.title.y = element_text(size = 12),
    axis.title.x = element_text(size = 12),
    plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5),
    legend.title = element_blank()
  )

4.4 Changing background elements

Background elements in a plot, such as grid lines, can make charts easier to read and interpret. Grid lines act as visual guides, helping you accurately estimate values and reducing the chance of errors when determining the position or value of a data point.

However, too much detail in the background can sometimes be distracting, especially if the chart includes annotations. In such cases, it might be better to simplify or completely remove the background to keep the focus on the data.

You can use the element_rect() function to adjust background colours and fills, and the element_line() function to modify any lines in the chart, such as grid lines or borders. These functions allow you to customise the appearance of your chart to make it clearer and more effective.

For example:

Code
pass_data_2020 <- raw_driving_pass_data %>%  
  dplyr::filter(Financial_Year == "2020")

ggplot(pass_data_2020, aes(x = Gender, y = Pass_rate)) +
   stat_boxplot(geom = "errorbar",
               # width of the errorbar
               width = 0.1, 
               # line weight
               lwd = 0.3) +
  geom_boxplot(lwd = 0.3) +
  labs(
    title = "Distribution of Driving test pass rate by gender",
    y = "% of passes",
    x = "Year"
    )

This boxplot would benefit from a plain background and horizontal grid lines to help read the distribution of pass rates between the genders. We can use the panel.background() and panel.grid.major() (and panel.grid.minor() for grid lines in between each major line).

Code
pass_data_2020 <- raw_driving_pass_data %>%  
  dplyr::filter(Financial_Year == "2020")

ggplot(pass_data_2020, aes(x = Gender, y = Pass_rate)) +
   stat_boxplot(geom = "errorbar",
               # width of the errorbar
               width = 0.1, 
               # line weight
               lwd = 0.3) +
  geom_boxplot(lwd = 0.3) +
  labs(
    title = "Distribution of Driving test pass rate by gender",
    y = "% of passes",
    x = "Year") + 
  scale_y_continuous(limits = c(40, 90)) +
  # let's create our theme 
  theme(
    panel.background = element_rect(fill = "white", colour = "black"),
    panel.grid.major = element_line(colour = "grey", linetype = "dashed",  linewidth = 0.2),
    panel.grid.minor = element_line(colour = "grey", linetype = "dashed",  linewidth = 0.2)
  )

4.4.1 Exercise

15:00

  1. Using the boxplot you made in Exercise 2.2.5, change the background and outline of the chart.
  2. Add grid lines to the boxplot. Play around with linetype, linewidth and the colour of the grid lines.
  3. Add the function coord_flip() as another layer. What happens?

Solution

Code
ggplot(transport_bar_data, aes(x = transport, y = usage)) + 
  stat_boxplot(geom = "errorbar",
               # width of the errorbar
               width = 0.1, 
               # line weight
               lwd = 0.3) +
  geom_boxplot(lwd = 0.3) +
  labs(
    title = "Distribution of transport usage levels between 2020 - 2022 compared to pre-Covid levels", 
    y = "Usage % compared to pre-Covid levels",
    x = "Transport") + 
  scale_y_continuous(breaks = c(0, 25, 50, 75, 100, 125), limits = c(0, 125)) +
  # let's create our theme 
  theme(
    panel.background = element_rect(fill = "white", colour = "black"),
    panel.grid.major = element_line(colour = "grey", linetype = "dashed",  linewidth = 0.2),
    panel.grid.minor = element_line(colour = "grey", linetype = "dashed",  linewidth = 0.2)) +
  # catch you on the flippity flip side
  coord_flip()

4.5 Legends

A final thing you may want to adjust is the legend.

Within the theme function you can adjust the legends:

  • placement (legend.position)
  • direction (legend.direction)
  • title (legend.title)
  • legend key size (legend.key.size)
  • legend key spacing (legend.spacing.x)
  • legend text (legend.text)
  • getting rid of a legend altogether
  • and so much more!

Let’s cover how to change the position of legend. To change the position, we use legend.positon() function.

There are two main ways to specify the position. The simplest way is to use one of these options - "top", "bottom", "right", "left". For example, legend.position = "top" will place the legend at the top of the plot. These options move the legend within the margin of the plot, but not inside the plot.

Another way to change the legend position is to use coordinates if you want it in a very specific position, but this does not look nice, and will make you confused.

Code
ggplot(data = good_driving_data) +
  geom_line(aes(y = value, x = Financial_Year, group = category, color = category)) +
  labs(
    title = "Total driving tests conducted and passed tests overtime", 
    x = "Year", 
    y = "Total") + 
  scale_fill_brewer(palette = "PuBuGn")+
  # using a sequence function for defining y axis breaks
  scale_y_continuous(breaks = seq(from = 0, to = 800, by = 100), limits = c(0, 800)) +
  # create theme 
  theme(
   axis.text.x = element_text(size = 10), 
   axis.text.y = element_text(size = 10),
   axis.title.y = element_text(size = 12),
   axis.title.x = element_text(size = 12),
   plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5),
   panel.background = element_rect(fill = "white"),
   # how to do horizontal gridlines only! 
  panel.grid.major.y = element_line(colour = "grey", linetype = "dashed", linewidth = 0.2),
   # legend stuff 
   legend.position = "top"
  )

We can also remove the legend title for this chart, since at the moment the word “category” is adding no value to the chart, and sounds confusing. To remove anything, use the element_blank() function.

What if we also want to remove that little grey background around the legend keys? We can use the legend.key parameter and combine it with element.rect() to set the legend key background to white.

Code
ggplot(data = good_driving_data) +
  geom_line(aes(y = value, x = Financial_Year, group = category, color = category)) +
  labs(
    title = "Total driving tests conducted and passed tests overtime", 
    x = "Year", 
    y = "Total") + 
  scale_fill_brewer(palette = "PuBuGn")+
  # using a sequence function for defining y axis breaks
  scale_y_continuous(breaks = seq(from = 0, to = 800, by = 100), limits = c(0, 800)) +
  # create theme 
  theme(
   axis.text.x = element_text(size = 10), 
   axis.text.y = element_text(size = 10),
   axis.title.y = element_text(size = 12),
   axis.title.x = element_text(size = 12),
   plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5),
   panel.background = element_rect(fill = "white"),
   # how to do horizontal gridlines only! 
   panel.grid.major.y = element_line(colour = "grey", linetype = "dashed", linewidth = 0.2),
  # legend is placed at the top of the chart
  legend.position = "top",
  # removes the legend title
  legend.title = element_blank(),
  # removes the grey background around the keys
  legend.key = element_rect(fill = "white")
  )

4.5.1 Exercise

15:00

  1. Using your line chart created in Exercise 2.2.7, add horizontal gridlines to the line chart.
  2. Move the legend to a different position.
  3. Change the legend title. Is it useful? If not, remove the legend title.
  4. Turn the background of the legend keys white.

Solution

Code
ggplot(data = transport_line_data) +
  geom_line(aes(y = mean_usage, x = month, group = transport, color = transport)) +
  labs(
    title = "Comparing TFL Bus and TFL Tube usage in 2022 compared to pre-Covid levels",
    y = "Usage %",
    x = "Month") + 
  scale_colour_brewer(palette = "Dark2") +
  # using a sequence function for defining y axis breaks
  scale_y_continuous(breaks = seq(from = 40 ,to = 100, by = 10), limits = c(40, 100)) +
  # create theme 
  theme(
   axis.text.x = element_text(size = 10),
   axis.text.y = element_text(size =10),
   axis.title.y = element_text(size = 11),
   axis.title.x = element_text(size = 11),
   plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5),
   panel.background = element_rect(fill = "white"),
   panel.grid.major.y =  element_line(colour = "grey", linetype = "dashed",  linewidth = 0.2),
   # legend stuff 
   legend.position = "top",
   legend.title = element_blank(),
   legend.key = element_rect(fill = "white", colour = "white")
  )

4.6 Make your theme an object

To use your theme repeatedly on different charts, you can assign it to an object, and then add it as a layer.

Code
my_theme <- theme(
  axis.text.x = element_text(size = 10),
  axis.text.y = element_text(size = 10),
  axis.title.y = element_text(size = 11),
  axis.title.x = element_text(size = 11),
  plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5),
  panel.background = element_rect(fill = "white"),
  panel.grid.major.y =  element_line(colour = "grey", linetype = "dashed", linewidth = 0.2),
   # legend stuff 
   legend.position = "top",
   legend.title = element_blank(),
   legend.key = element_rect(fill = "white", colour = "white")
  )

And now add you can add your customed theme to any plot:

Code
ggplot(data = transport_line_data) + 
  geom_line(aes(y = mean_usage, x = month, group = transport, color = transport)) +
  labs(
    title = "Comparing TFL Bus and TFL Tube usage in 2022 compared to pre-Covid levels",
    y = "Usage %",
    x = "Month") + 
  scale_colour_brewer(palette = "Dark2") +
  scale_y_continuous(breaks = seq(from = 40 ,to = 100, by = 10), limits = c(40, 100)) +
  my_theme

4.7 Using the dftplotr package

Now that you know how to create your own theme, you may also find it very useful to know there is a series of DfT themes available. You could even use the topics explored above to adapt the DfT themes to meet your visualisation needs.

dftplotr:: is an R package to provide standardised charts formatting in ggplot2. It includes theme functions which meet Government Statistical Service (GSS) best practice guidance. It also includes colour palettes based on DfT corporate branding which meet WCAG 2.0 accessibility standards, and are distinguishable in greyscale. This package is public and can be used by anyone.

Read more about the package on its README section on Github.

The package can be installed directly from Github using the remotes::install_github() function.

Code
install.packages("remotes")
remotes::install_github("departmentfortransport/dftplotr")

Once it has installed, load the package:

Code
library(dftplotr)

The package contains the following functions:

Function What its for
scale_colour_dft Applies one of a range of DfT palettes to a ggplot line chart (or other chart which groups by colour)
scale_fill_dft Applies one of a range of DfT palettes to a ggplot line chart (or other chart which groups by fill)
display_palette A visual output which shows all of the colours contained in the selected DfT palette
theme_general_dft A standardised theme to all ggplot charts.
theme_bar_dft A standardised theme to ggplot bar chart. There are options to pick other DfT color palettes and top flip the x and y axis.
theme_line_dft A standardised theme to a ggplot line chart. There are options to pick other DfT color palettes.

For detailed help on how to use the functions in the package, use the Help page for the function (e.g. ?scale_fill_dft()).

In addition to DfT themes, the package also comes with 9 pre-loaded palettes. These contain colours taken from the DfT colour scheme, and have also been selected to meet accessibility standards.

The palettes and the colours in them can be seen here, or visualised within the package by calling display.palette() and the palette name.

Let’s see an example:

It is a simple process of adding the function as another layer and specifying the palette.

Code
 driving_bar_chart + 
  # adding a dft colour palette
  dftplotr::scale_colour_dft(palette = "electric.brights")   

In this example, a theme and a colour palette from the dftplotr:: package are applied to the chart. This is done by adding two additional function layers. It is important to note that the code executes in order, meaning any previously set theme arguments will be overwritten by the new theme’s settings.

Code
driving_line_chart +
  dftplotr::scale_fill_dft(palette = "travel.direction") +
  dftplotr::theme_general_dft()

4.7.1 Exercise

15:00

  1. Double check you have installed and loaded the dftplotr:: package into your environment.
  2. Apply an accessible DfT colour palette and or theme to one of your charts from a previous exercise.

Solution

Code
ggplot(data = transport_line_data) +
  geom_line(aes(y = mean_usage, x = month, group = transport, color = transport)) +
  labs(
    title = "Comparing TFL Bus and TFL Tube usage in 2022 compared to pre-Covid levels",
    y = "Usage %",
    x = "Month") + 
  # adding dft colour palette
  dftplotr::scale_colour_dft(palette = "joyful.journey") +
  scale_y_continuous(breaks = seq(from = 0 ,to = 100, by = 10), limits = c(0, 100)) +
  # adding dft theme 
  dftplotr::theme_general_dft()


This is a good opportunity to take a 10-minute break away from the computer to refresh your mind, stretch, and reset before continuing onto Chapter 5.