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
- Using the stacked bar chart you made in Exercise 2.2.3, add a
title
,x
andy
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:
- Change the text aesthetics, such as size, formatting, angle or placement of your:
x
axis textx
axis titley
axis texty
axis title- chart title
- 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
- Using the boxplot you made in Exercise 2.2.5, change the background and outline of the chart.
- Add grid lines to the boxplot. Play around with
linetype
,linewidth
and the colour of the grid lines. - 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
- Using your line chart created in Exercise 2.2.7, add horizontal gridlines to the line chart.
- Move the legend to a different position.
- Change the legend title. Is it useful? If not, remove the legend title.
- 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.
Once it has installed, load the package:
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
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
4.7.1 Exercise
15:00
- Double check you have installed and loaded the
dftplotr::
package into your environment. - 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.