Please complete the Student Voice section on NOW.
Please complete the Student Voice section on NOW.
By the end of this session, you should be able to:
ggplot2 visualisations using labels, legends, colour, and simple themes.Download the Week 5 files from NOW. Unzip the folder, then move the unzipped Week 5 folder into your existing RStudio Project folder.
The session has two practical blocks.
| Block | Main work | Files |
|---|---|---|
| 1 | Make plots easier to understand with labels, legends, colour, and aesthetics. |
01_labels_and_legends.Rmd02_colours_and_aesthetics.Rmd selected parts
|
| 2 | Start a formative assessment figure and decide what the plot should show. |
04_formative_practice_provided_data.Rmdor 05_formative_planning_own_data.Rmd
|
03_themes.Rmd is optional/homework/reference material for after the session.
The priority today is not to try every customisation option. It is to make better decisions about which changes help the viewer.
What do you notice first? Which labels or colours would you change?
Source: Office for National Statistics, Figure 3 (2025). Decile 1 = most deprived areas; 10 = least deprived.
In Week 4, variable type helped us choose geoms. In Week 5, it helps us choose aesthetics and scales.
| Variable type | Good aesthetic choices | Be careful with |
|---|---|---|
| categorical groups | colour, fill, shape, linetype, facets | too many colours or shapes |
| ordered categories | ordered colour, position, facets | unordered palettes |
| continuous values | position, colour gradient, size | size can be hard to compare |
| binary variables | colour, shape, linetype | redundant legends |
Aesthetic mappings should make the important comparison easier, not just make the plot more colourful.
Customisation should help the viewer answer the question.
| Customisation | Helps the viewer… | Ask yourself… |
|---|---|---|
| labels | know what the variables, units, and groups mean | could this plot stand alone in a report? |
| legends and facets | understand which observations belong to which group | are the group names meaningful? |
| colour and aesthetics | compare categories, ordered values, or continuous values | does the aesthetic fit the variable type? |
| scales | read values fairly and accurately | could the scale mislead the comparison? |
| themes | remove unnecessary distraction | did this remove clutter or add decoration? |
| export choices | make the figure readable at final size | are text, points, and aspect ratio still clear? |
Week 5 is about defensible visualisation choices, not polishing plots for its own sake.
Tufte’s principles are not just about making sparse plots. They ask whether every visible element helps the viewer understand the data.
| Principle | What customisation can do | Example Week 5 choice |
|---|---|---|
| increase data-ink | make the data easier to see than the decoration | lighten or remove heavy grid lines |
| reduce chartjunk | remove marks that do not support the comparison | avoid decorative themes and unnecessary colours |
| support comparison | keep related plots comparable | use consistent scales, facets, and labels |
| guide interpretation | help viewers notice the intended pattern | use titles, subtitles, captions, and direct labels |
| avoid distortion | make readings fair and transparent | choose appropriate axes, scales, and grouping aesthetics |
Customising a plot is an argument about what matters in the data.
Revisiting Tufte’s Principles (Tufte, 2001):
The point is not that every plot should be minimal. The point is that every customisation should earn its place.
ggplot2 DecisionsIn ggplot2, these principles become specific choices we can make and justify.
| Tufte principle | ggplot2 decision |
|---|---|
| increase data-ink | use quiet themes, lighter grids, and visible data marks |
| reduce chartjunk | avoid decorative palettes, heavy backgrounds, and unnecessary legends |
| support comparison | use facets, consistent scales, and aligned axes |
| guide interpretation | write meaningful titles, axis labels, legend labels, and captions |
| avoid distortion | choose sensible scales, transparency, jittering, or grouping aesthetics |
The grammar of graphics helps because each layer can be changed deliberately: data, mapping, geom, scale, coordinate system, facet, and theme.
titlesubtitlecaptiontagxycolour, shape, linetype, fillggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
labs()
titlesubtitlecaptiontagxycolour, shape, linetype, fillggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
labs(title =
"Reaction time and duration")
titlesubtitlecaptiontagxycolour, shape, linetype, fillggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
labs(title =
"Reaction time and duration",
subtitle = "Participant-level averages")
titlesubtitlecaptiontagxycolour, shape, linetype, fillggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
labs(caption = "Data: picture naming norms")
titlesubtitlecaptiontagxycolour, shape, linetype, fillggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
labs(tag = "A")
titlesubtitlecaptiontagxycolour, shape, linetype, fillggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
labs(x = "Production duration (ms)",
y = "Reaction time (ms)")
titlesubtitlecaptiontagxycolour, shape, linetype, fillggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
labs(colour = "Response mode")
Open 01_labels_and_legends.Rmd.
Complete Tasks 1-3 (about 12 minutes): choose axis labels, improve the legend, and write a title.
Keep one labelled figure and one sentence explaining your choice.
Stop at “In-class stopping point”. The remaining label examples are optional.
Colour can represent different kinds of information.
| Colour property | Best for | Example |
|---|---|---|
| Hue | categories | speech vs writing |
| Brightness / lightness | ordered or continuous values | low to high vocabulary |
| Diverging colour | values around a meaningful midpoint | below vs above average |
Use different hues for different kinds of things. Use light-to-dark progressions for more/less.
scale_colour_discretescale_colour_continuousscale_colour_manualcolourggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
scale_colour_discrete(
labels = c("Spoken output",
"Written output"))
ggthemes::scale_colour_colorblind()ggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
scale_colour_colorblind(
labels = c("Spoken output",
"Written output"))
ggthemesggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
scale_colour_manual(
labels = c("Spoken output",
"Written output"),
values = c("#0072B2", "#D55E00"))
scales::show_col() previews colours# RGB codes of "colorblind" function
mycolours <- c("#000000", "#E69F00",
"#56B4E9", "#009E73",
"#F0E442", "#0072B2",
"#D55E00", "#CC79A7")# RGB codes of "colorblind" function scales::show_col(colorblind_pal()(8))
labels changes the legend wordingvalues controls the corresponding coloursggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
scale_colour_manual(
labels = c("Spoken output",
"Written output"),
values = mycolours[1:2])
Each version shows the same relationship. Which would you use, and what trade-off does it make?
When does another aesthetic help, and when does it become clutter?
Open 02_colours_and_aesthetics.Rmd.
Complete Tasks 1-2 (about 12 minutes): choose a palette and compare overlapping age distributions.
Record which version you would use and what the viewer can compare more easily.
Stop at “In-class stopping point”. Manual palette exploration is optional.
ggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality)
ggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality,
labeller = label_both)
d_spellname <- mutate(d_spellname,
modality = recode(modality,
speech = "Spoken output",
writing = "Written output"))
ggplot(d_spellname, aes(x = dur, y = rt)) +
geom_point() +
facet_grid(~modality)
ggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality, labeller = label_both)
Start with a complete theme, then change individual elements with theme().
| Complete theme | Starting appearance |
|---|---|
theme_grey() |
grey panel and white grid lines |
theme_bw() |
white panel, grid lines, and a border |
theme_classic() |
axis lines without grid lines |
theme_minimal() |
grid lines without a panel background |
theme_light() |
light grid lines and a border |
theme_dark() |
dark panel and lighter grid lines |
ggthemes adds starting points such as theme_clean() and theme_tufte().
Choose a starting point that makes the comparison easy to read.
ggplot2 Defaultggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality) +
theme_grey(base_size = 11)
theme_minimal(): No Panel Backgroundggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality) +
theme_minimal(base_size = 14)
theme_light(): Light Grid And Borderggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality) +
theme_light(base_size = 14)
theme_dark(): A Dark Panel Backgroundggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality) +
theme_dark(base_size = 14)
ggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality) +
theme_clean()
axislegendpanelplotstripggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme()
axis.text
axis.text.xaxis.text.yaxis.title
axis.title.xaxis.title.yggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme(axis.text = element_text(
face = "italic"))
axis.text
axis.text.xaxis.text.yaxis.title
axis.title.xaxis.title.yggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme(axis.title = element_text(
face = "bold"))
axis.text
axis.text.xaxis.text.yaxis.title
axis.title.xaxis.title.yggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme(axis.title.y = element_text(
face = "bold"))
legend.backgroundlegend.marginlegend.spacinglegend.keylegend.textlegend.titlelegend.positionlegend.orientationlegend.justificationlegend.boxggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
theme()
legend.backgroundlegend.marginlegend.spacinglegend.keylegend.textlegend.titlelegend.positionlegend.orientationlegend.justificationlegend.boxggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
theme(legend.position = "top")
legend.backgroundlegend.marginlegend.spacinglegend.keylegend.textlegend.titlelegend.positionlegend.orientationlegend.justificationlegend.boxggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
theme(legend.position = "top",
legend.justification = "right")
legend.backgroundlegend.marginlegend.spacinglegend.keylegend.textlegend.titlelegend.positionlegend.orientationlegend.justificationlegend.boxggplot(d_spellname, aes(y = rt,
x = dur,
colour = modality)) +
geom_point() +
theme(legend.position = c(.15, .8))
panel.backgroundpanel.borderpanel.spacingpanel.grid
panel.grid.majorpanel.grid.minorggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme()
panel.backgroundpanel.borderpanel.spacingpanel.grid
panel.grid.majorpanel.grid.minorggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme(panel.background =
element_blank())
plot.backgroundplot.marginplot.titleplot.subtitleplot.captionplot.tagggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme()
plot.backgroundplot.marginplot.titleplot.subtitleplot.captionplot.tagggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme(plot.background = element_rect(
fill = "pink"))
plot.backgroundplot.marginplot.titleplot.subtitleplot.captionplot.tagggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
theme(plot.background = element_rect(
fill = "pink"),
plot.margin = unit(c(2, 2, 2, 2),
"cm"))
plot.backgroundplot.marginplot.titleplot.subtitleplot.captionplot.tagggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
labs(title = "I'm a title") +
theme(plot.title = element_text(
colour = "pink"))
plot.backgroundplot.marginplot.titleplot.subtitleplot.captionplot.tagggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
labs(caption = "I'm a caption") +
theme(plot.caption = element_text(
face = "italic"))
strip.backgroundstrip.placementstrip.textggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality,
labeller = label_both) +
theme()
strip.backgroundstrip.placementstrip.textggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality,
labeller = label_both) +
theme(strip.background =
element_blank())
strip.backgroundstrip.placementstrip.textggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality,
labeller = label_both) +
theme(strip.background = element_rect(
fill = "forestgreen"))
strip.backgroundstrip.placementstrip.textggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality,
labeller = label_both) +
theme(strip.background = element_rect(fill = "forestgreen"),
strip.text = element_text(colour = "white", hjust = 0))
strip.backgroundstrip.placementstrip.textggplot(d_spellname, aes(y = rt,
x = dur)) +
geom_point() +
facet_grid(~modality,
labeller = label_both)+
theme(strip.background = element_rect(fill = "forestgreen"),
strip.text = element_text(colour = "white", hjust = 0,
face = "bold", size = 16, angle = 180))
03_themes.Rmd is optional/homework/reference material.
If you have time, use it to compare themes and choose a quiet starting point for an assessment-style figure. The aim is not to design a personal style from scratch during class.
ggplot2 CheatSheetFor support with this week’s material, use:
ggplot2 online book sections on scales, labels, themes, and saving plots as optional extra support. Online bookThis is the main second-block task. Use one of the two templates in the Week 5 files:
04_formative_practice_provided_data.Rmd if you want to practise with the provided Blomkvist dataset.05_formative_planning_own_data.Rmd if you already have a candidate dataset.Start by deciding what your first figure should help the viewer compare.
Use the next 25 minutes to:
You can run chunks while developing the figure.
Select a behavioural dataset, such as reaction times, survey responses, or decision-making data.
Feedback focus:
Choose one or two decisions in your figure. Explain how they help answer your question and what the figure still hides.
| Decision | Options you could compare |
|---|---|
| Showing variation | raw points, a summary, or both |
| Distinguishing groups | colour, colour and shape, or separate facets |
| Showing overlap | opaque fill, transparent fill, or outlines |
| Making text useful | variable names or descriptive labels with units; a question or finding as the title |
| Keeping useful structure | keep, lighten, or remove grid lines; choose a starting theme |
Example: “I used transparent fills so the overlapping age distributions remain visible, but the curves do not show how many participants are in each group.”
Andrews, M. (2021). Doing data science in R: An introduction for Social Scientists. SAGE Publications Ltd.
Tufte, E. R. (2001). The visual display of quantitative information (2nd ed.). Graphics Press.