Week 2: Your First ggplot
From base R to the grammar of graphics
Introduction
In the content session, you learned about summarising data (mean, median, spread), honest data visualisation, and the grammar of graphics — the idea that a plot is built from layers: data, aesthetics, and geometries.
These two exercises let you practise the ggplot2 syntax before the application session, where you’ll build briefing-quality figures.
Goals:
- Build a histogram with
geom_histogram() - Build a scatter/line plot with colour mapping
- Get comfortable with the
ggplot(data, aes(...)) + geom_*()pattern
In GEOL1151 you built plots imperatively — one command per element:
plt.plot(year, bio, label="Bioenergy")
plt.xlabel("Year")
plt.legend()
plt.show()ggplot2 works declaratively: you describe what the plot is, and ggplot2 figures out how to draw it.
| Concept | matplotlib | ggplot2 |
|---|---|---|
| Start a plot | plt.figure() |
ggplot(data, aes(...)) |
| Map columns to axes | plt.plot(x, y) |
aes(x = year, y = value) |
| Choose a geometry | plt.scatter(...) |
+ geom_point() |
| Add layers | Separate calls | + operator |
| Labels | plt.xlabel(...) |
+ labs(x = ...) |
| Legend | Manual label= args |
Automatic from colour/fill mapping |
The biggest shift: aes() does the mapping. You tell ggplot2 which columns mean what (x position, y position, colour), then add a geometry that knows how to draw those mappings. No quoting column names inside aes() — just bare names like aes(x = year, y = bioenergy_twh).
Exercise 1: Distribution of bioenergy generation
Last week you plotted bioenergy over time. But what does the distribution look like? Make a histogram of bioenergy_twh to see how the values are spread. What shape is the distribution — and why might it look that way?
The first argument to ggplot() is the data frame. Inside aes(), map x to the column you want to histogram — no y needed.
ggplot(elec, aes(x = bioenergy_twh)) +
geom_histogram(bins = 10) + ...ggplot(elec, aes(x = bioenergy_twh)) +
geom_histogram(bins = 10) +
labs(x = "Bioenergy generation (TWh)",
y = "Count",
title = "Distribution of UK bioenergy generation (2000–2024)")The distribution is right-skewed (or bimodal), with a cluster of low values (the early 2000s, when bioenergy was small) and a cluster of higher values (post-2015). This shape reflects a quantity that grew rapidly over time — it’s not random variation around a fixed average. The mean (~18 TWh) would be a misleading summary here.
Exercise 2: Generation over time, by fuel
Now make a scatter plot of electricity generation over time, with colour showing the fuel type. This exercise uses just two fuels — bioenergy and coal — to keep the syntax manageable.
The trick: you need one geom_point() layer per fuel, each with its own aes(). (In the application session, you’ll learn a cleaner way to handle multiple groups.)
Each geom_point() layer maps y to a different column. Setting colour = "Bioenergy" (a constant string) inside aes() creates a legend entry. Do the same for coal.
geom_point(aes(y = coal_twh, colour = "Coal", shape = "Coal"))ggplot(elec, aes(x = year)) +
geom_point(aes(y = bioenergy_twh, colour = "Bioenergy",
shape = "Bioenergy")) +
geom_point(aes(y = coal_twh, colour = "Coal",
shape = "Coal")) +
labs(x = "Year",
y = "Generation (TWh)",
colour = "Fuel", shape = "Fuel",
title = "UK electricity: bioenergy vs coal")Bioenergy rises while coal collapses — the same pattern you saw in Week 1, but now with a colour legend that makes the comparison explicit. Notice how ggplot2 automatically creates the legend from the colour aesthetic. Try adding geom_line() layers with the same aes() to connect the dots.
Next steps
You’ll use these ggplot2 skills extensively in the application session, where you’ll build the figures for your policy briefing.