Primer 5 · Your first plot
ggplot2, one layer at a time
This page needs about 37 MB on first load, because drawing plots in a browser means downloading ggplot2 and everything it depends on. On a fast connection expect around 30 seconds before the first cell will run; on a slow or shared connection, considerably longer.
Do this one at home, not at IISc. Your browser keeps the download for 7 days, so a page you open this week costs nothing on Monday.
If the cells never become ready, your network is blocking it. The static R primer covers the same material for RStudio.
There is no Stop button. If you write a loop that never ends, reload the page.
ggplot2 is not a menu of chart types. It is a grammar: you say what data to use, which variable maps to which visual property, and what shape to draw. Three decisions, three pieces of code.
Run the cells from the top. The first one builds the data that all the others use. If a later cell says object not found, you have skipped it.
The three pieces
Read it in three parts:
| Part | Says |
|---|---|
ggplot(survey, ...) |
use this dataset |
aes(x = age, y = sbp) |
put age on the x axis, blood pressure on y |
geom_point() |
draw each row as a point |
aes() is short for aesthetic mapping: it connects a column in your data to something you can see. geom_ functions say what shape to draw.
+, not |>
This is the one place in the tidyverse where the pipe does not apply. ggplot2 is older than the pipe and uses + to add layers. Using |> here gives an error that does not explain itself.
Mapping a third variable
Anything inside aes() is driven by the data. Map smoker to colour and ggplot2 splits the points and builds a legend for you:
Note where size = 3 went: outside aes(). That is the rule, and it is easy to get wrong the first time.
- Inside
aes()— “this property comes from a column”. - Outside
aes()— “this property is a fixed setting for the whole layer”.
colour = smoker inside means colour by the smoking column. colour = "blue" outside would mean make every point blue.
Distributions: the histogram
One variable, so only x. A histogram counts how many observations fall in each bin.
Change binwidth to 5, then to 25, and run it again. The data has not changed; the story has. Always say what binwidth you used.
Comparing groups: the boxplot
Each box here rests on 6 or 7 observations. A box drawn over that few points looks as authoritative as one drawn over 600. Add the points on top so the reader can see how thin the evidence is:
ggplot(survey, aes(x = district, y = sbp)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter(width = 0.15, alpha = 0.6)Counting categories: the bar chart
geom_bar() counts rows for you. Give it only an x.
If you already have the counts in a column, use geom_col() instead, which draws the number you give it rather than counting rows.
Summarise first, then plot
Most real plots are not drawn from the raw rows. You summarise with the Primer 4 verbs, then pipe the result into ggplot().
Note the join in the middle: |> between the dplyr steps, then + between the ggplot layers. That switch is the whole trick.
geom_col() here, not geom_bar(): you have already worked out the number, so you want it drawn, not counted again.
Add filter(n >= 7) as a line after summarise() and run it again. Hassan disappears, because only 6 people there were surveyed. The pipeline is a list of steps, so you can insert one anywhere.
It has to go after summarise(), because that is the step that creates the n column. Put it before and R will tell you n does not exist.
A bar chart of raw rows can only ever show counts. The moment you want a percentage, a mean, a rate or a risk on the y axis, you have to compute it first. That is why dplyr and ggplot2 are taught together.
Labels are not decoration
A plot that needs you standing next to it is not finished. labs() is the cheapest improvement you can make to one.
Two habits worth copying from that cell:
- The title says what the reader should conclude, not what the axes are. “Systolic blood pressure rises with age” beats “Scatter plot of SBP by age”.
- The caption says the data is simulated. If your data is real, the caption is where the source goes.
What you should now be able to do
- Build a plot from data, an
aes()mapping, and ageom_. - Tell when something goes inside
aes()(it comes from a column) and when it goes outside (it is a fixed setting). - Draw a scatter plot, a histogram, a boxplot and a bar chart.
- Add layers with
+, not|>. - Label a plot so it survives being separated from you.
You have finished the R primers
→ Step 4: the epidemiology primers. They are lighter than this page.