Hands-on Exercise 2

Author

Serene Cheng

Published

April 22, 2026

Modified

April 23, 2026

Reflections

From this exercise, I learned that ggplot2 can be extended further with additional packages to make visualisations clearer, more polished, and more effective. I explored how packages such as ggrepel, ggthemes, hrbrthemes, and patchwork can improve the way charts are labelled, styled, and combined into a single figure. Overall, this exercise helped strengthen both my technical understanding of data visualisation and my ability to present charts in a more professional and readable way.

Getting Started

Installing and loading the required libraries

Four R packages will be used on top of tidyverse.

  • ggrepel: an R package provides geoms for ggplot2 to repel overlapping text labels.

  • ggthemes: an R package provides some extra themes, geoms, and scales for ‘ggplot2’.

  • hrbrthemes: an R package provides typography-centric themes and theme components for ggplot2.

  • patchwork: an R package for preparing composite figure created using ggplot2.

pacman::p_load(ggrepel, patchwork, 
               ggthemes, hrbrthemes,
               tidyverse)

Importing data

Next up, we will import the data (looks familiar? Its the same dataset as the one in Hands-on Exercise 1!

exam_data <- read_csv("data/Exam_data.csv")

Total of seven attributes in the exam_data table data frame. Four of them are categorical data type and the other three are in continuous data type.

  • The categorical attributes are: ID, CLASS, GENDER and RACE.

  • The continuous attributes are: MATHS, ENGLISH and SCIENCE.

Beyond ggplot2 Annotation: ggrepel

ggplot(data=exam_data, 
       aes(x= MATHS, 
           y=ENGLISH)) +
  geom_point() +
  geom_smooth(method=lm, 
              linewidth=0.5) +  
  geom_label(aes(label = ID), 
             hjust = .5, 
             vjust = -.5) +
  coord_cartesian(xlim=c(0,100),
                  ylim=c(0,100)) +
  ggtitle("English scores versus Maths scores for Primary 3")

ggplot(data=exam_data, 
       aes(x= MATHS, 
           y=ENGLISH)) +
  geom_point() +
  geom_smooth(method=lm, 
              linewidth=0.5) +  
  geom_label(aes(label = ID), 
             hjust = .5, 
             vjust = -.5) +
  coord_cartesian(xlim=c(0,100),
                  ylim=c(0,100)) +
  ggtitle("English scores versus Maths scores for Primary 3")

ggrepel  is an extension of ggplot2 package which provides geoms for ggplot2 to repel overlapping text as in our examples on the right.

We can simply replace geom_text() by geom_text_repel() and geom_label() by geom_label_repel.

Working with ggrepel

ggplot(data=exam_data, 
       aes(x= MATHS, 
           y=ENGLISH)) +
  geom_point() +
  geom_smooth(method=lm, 
              linewidth=0.5) +  
  geom_label_repel(aes(label = ID), 
                   fontface = "bold") +
  coord_cartesian(xlim=c(0,100),
                  ylim=c(0,100)) +
  ggtitle("English scores versus Maths scores for Primary 3")

ggplot(data=exam_data, 
       aes(x= MATHS, 
           y=ENGLISH)) +
  geom_point() +
  geom_smooth(method=lm, 
              linewidth=0.5) +  
  geom_label_repel(aes(label = ID), 
                   fontface = "bold") +
  coord_cartesian(xlim=c(0,100),
                  ylim=c(0,100)) +
  ggtitle("English scores versus Maths scores for Primary 3")

Beyond ggplot2 Themes

ggplot2 comes with eight built-in themes, they are: theme_gray(), theme_bw(), theme_classic(), theme_dark(), theme_light(), theme_linedraw(), theme_minimal(), and theme_void().

ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  theme_gray() +
  ggtitle("Distribution of Maths scores") 

ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  theme_gray() +
  ggtitle("Distribution of Maths scores") 

Refer to this link to learn more about ggplot2 Themes

Working with ggtheme package

ggthemes provides ‘ggplot2’ themes that replicate the look of plots by Edward Tufte, Stephen Few, Fivethirtyeight, The Economist, ‘Stata’, ‘Excel’, and The Wall Street Journal, among others. In the example below, The Economist theme is used.

ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  ggtitle("Distribution of Maths scores") +
  theme_economist()

ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  ggtitle("Distribution of Maths scores") +
  theme_economist()

Click this vignette to learn more.

Working with hrbthems package

hrbrthemes package provides a base theme that focuses on typographic elements, including where various labels are placed as well as the fonts that are used.

ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  ggtitle("Distribution of Maths scores") +
  hrbrthemes::theme_ipsum(base_family = "sans")

ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  ggtitle("Distribution of Maths scores") +
  hrbrthemes::theme_ipsum(base_family = "sans")

Below centers around productivity for a production workflow. This “production workflow” is the context for where the elements of hrbrthemes should be used. Click this vignette to learn more.

ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  ggtitle("Distribution of Maths scores") +
  theme_ipsum(
    base_family = "sans",
    axis_title_size = 18,
    base_size = 15,
    grid = "Y"
  )

ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  ggtitle("Distribution of Maths scores") +
  theme_ipsum(
    base_family = "sans",
    axis_title_size = 18,
    base_size = 15,
    grid = "Y")

What can we learn from the code chunk above?

  • axis_title_size argument is used to increase the font size of the axis title to 18,

  • base_size argument is used to increase the default axis label to 15, and

  • grid argument is used to remove the x-axis grid lines.

Beyond Single Graph

Not unusual that multiple graphs are required to tell compelling visual story. Several ggplot2 extensions that provides functions to compose figure with multiple graphs. I learn how to create composite plot by combining multiple graphs.

p1 <- ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") + 
  coord_cartesian(xlim=c(0,100)) +
  ggtitle("Distribution of Maths scores")

p1

p1 <- ggplot(data=exam_data, 
             aes(x = MATHS)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") + 
  coord_cartesian(xlim=c(0,100)) +
  ggtitle("Distribution of Maths scores")

Next step,

p2 <- ggplot(data=exam_data, 
             aes(x = ENGLISH)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  coord_cartesian(xlim=c(0,100)) +
  ggtitle("Distribution of English scores")

p2

p2 <- ggplot(data=exam_data, 
             aes(x = ENGLISH)) +
  geom_histogram(bins=20, 
                 boundary = 100,
                 color="grey25", 
                 fill="grey90") +
  coord_cartesian(xlim=c(0,100)) +
  ggtitle("Distribution of English scores")

Lastly, draw scatterplot for English vs Maths score

p3 <- ggplot(data=exam_data, 
             aes(x= MATHS, 
                 y=ENGLISH)) +
  geom_point() +
  geom_smooth(method=lm, 
              size=0.5) +  
  coord_cartesian(xlim=c(0,100),
                  ylim=c(0,100)) +
  ggtitle("English scores versus Maths scores for Primary 3")

p3

p3 <- ggplot(data=exam_data, 
             aes(x= MATHS, 
                 y=ENGLISH)) +
  geom_point() +
  geom_smooth(method=lm, 
              size=0.5) +  
  coord_cartesian(xlim=c(0,100),
                  ylim=c(0,100)) +
  ggtitle("English scores versus Maths scores for Primary 3")

Creating Composite Graphics: pathwork methods

This section discusses several ggplot2 extension packages that can be used to combine multiple graphs into one composite figure, including grid.arrange()  from gridExtra and plot_grid()from cowplot . The main focus is on  patchwork, which is a ggplot2 extension package designed to make plot combination easier and more flexible.

One key advantage of patchwork is its simple syntax, which allows different plot layouts to be created easily. For instance, the plus sign (+) is used for a two-column layout, parentheses () are used to group plots together, and the division sign (/) is used to arrange plots in two rows.

Combining 2 ggplot2 graphs

Figure in the tabset below shows a composite of two histograms created using patchwork. Note how simple the syntax used to create the plot!

p1 + p2

p1 + p2

Combining 3 ggplot2 graphs

(p1 / p2) | p3

(p1 / p2) | p3

  • “/” operator to stack two ggplot2 graphs,

  • “|” operator to place the plots beside each other,

  • “()” operator the define the sequence of the plotting.

To learn more about, refer to Plot Assembly.

Creating composite figure with tag

To identify subplots in text, patchwork also provides auto-tagging capabilities as shown in the figure below

((p1 / p2) | p3) + 
  plot_annotation(tag_levels = 'I')

((p1 / p2) | p3) + 
  plot_annotation(tag_levels = 'I')

Creating figure with insert

With insert_element() of patchwork, we can place one or several plots or graphic elements freely on top or below another plot.

p3 + inset_element(p2, 
                   left = 0.02, 
                   bottom = 0.7, 
                   right = 0.5, 
                   top = 1)

p3 + inset_element(p2, 
                   left = 0.02, 
                   bottom = 0.7, 
                   right = 0.5, 
                   top = 1)

Creating a composite figure by using patchwork and ggtheme

Figure below is created by combining patchwork and theme_economist() of ggthemes package discussed earlier.

patchwork <- (p1 / p2) | p3
patchwork & theme_economist()

p3 + inset_element(p2, 
                   left = 0.02, 
                   bottom = 0.7, 
                   right = 0.5, 
                   top = 1)

Reference