Session 3 :: Data Visualization with ggplot2
Bioinformatics Core Facility CECAD
2026-09-24
Session 3 :: Data Visualization with ggplot2
By the end of this session you should be able to:
ggplot2 is worth learning on top of base R graphics.ggplot2 call as three parts: data, aesthetic mapping, and geoms.ggplot2, and see the two side by side.The motivation
For a single, simple plot the two are close. The difference shows up as soon as the plot needs to grow.
Before we go further, compare these two base R calls:
cols <- c("#E69F00", "#0072B2", "#009E73")
# A
plot(iris$Sepal.Length, iris$Sepal.Width, col = "blue")
# B
plot(iris$Sepal.Length, iris$Sepal.Width, col = cols[iris$Species])One of these sets a fixed colour; the other looks up a colour per observation from Species. Which is which, and what would you need to add to B to also get a legend?
A is fixed — every point is the same blue, no matter what. B is mapped — the colour depends on the data, so it varies row by row, and B is also the one that needs you to build a legend by hand in base R. ggplot2 makes exactly this distinction explicit: mappings go inside aes(), fixed values go outside it. We will put this to work right away.
Three common tasks when a plot grows:
legend(); ggplot2 generates guides from mappings.par() or helpers; ggplot2 uses themes.We will compare these workflows using the same data.
A ggplot2 plot is built from a few named pieces:
| Piece | Question it answers | Example |
|---|---|---|
| data | Which data frame? | iris |
aesthetic mapping (aes) |
Which column drives which visual property? | x = Sepal.Length, colour = Species |
| geom | How should the data be displayed? | geom_point(), geom_bar() |
| facet | Split into panels by which variable? | facet_wrap(~ Species) |
| scale | How do data values map to a visual range — position, colour, size? | scale_colour_*(), scale_x_log10() |
| theme | How does the plot look — fonts, gridlines, background — independent of the data? | theme_minimal(), theme_classic() |
Some geoms display individual observations; others display summaries. Layers inherit data and mappings from ggplot() unless overridden.
ggplot() callggplot(data = iris, # 1. the data
mapping = aes(x = Sepal.Length, # 2. map columns
y = Sepal.Width, # to visual
colour = Species)) + # properties
geom_point() + # 3. draw points
labs(x = "Sepal length (cm)", # 4. labels
y = "Sepal width (cm)")data and mapping set once in ggplot() are inherited by every layer, unless a layer overrides them.+ to add layers or settings. Later drawing layers appear on top.aes() is for mappings from data. A fixed value (all points blue) goes outside aes().base R on the left, ggplot2 on the right
Base R — formula interface, own legend
Both plots use column names for axis labels. In ggplot2, mapping colour = factor(cyl) assigns colours and creates the legend.
Base R — tabulate first, then plot
geom_bar() counts the rows in each category for you. If you already have the numbers, use geom_col() instead.
Define the bin boundaries once for both plots:
Both plots use the same explicit breakpoints, so the bins are comparable.
The base R formula mpg ~ cyl becomes the mapping aes(factor(cyl), mpg).
Set up the panels, use shared limits, and plot each species.
facet_wrap() handles the panel layout, labels, and shared scales.
Base R
ggplot2
Both show one panel per species with common limits across panels.
Base R — fit and draw each line
geom_smooth(method = "lm") fits one line per colour group, because the colour = Species mapping already split the data — the same fit the base R loop builds by hand.
Base R — share settings with par()
Both systems support reusable styling. ggplot2 packages plot styling in named theme objects, such as theme_classic().
The point colour is fixed outside aes(); it is a layer setting.
plot() writes straight to the graphics device — nothing is left afterward to reuse. A ggplot2 call instead returns an object.
p <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
p2 <- p + facet_wrap(~ cyl) # p2 is p, extended
p # p itself is unchanged+ returns a new object rather than changing p in place — p2 gains the facets, p does not.p auto-prints at the console or top level of a chunk. Inside a function or loop, that does not happen automatically — call print(p) explicitly.Base R — open a device, plot, close it
Choose dimensions, file format, and raster resolution to match the publication’s requirements. ggsave() infers the format from the extension.
ggplot2 is not a replacement for every situation:
plot() or hist() is fast and needs no packages.plot(hclust_result) for a dendrogram or image() for a matrix.The clearest advantages shown here are automatic legends, faceting, and reusing mappings across layers. Both systems can produce publication figures.
Three things to take away
ggplot2 plot is data + an aesthetic mapping + geoms, combined with +.aes() is for values that come from the data; fixed values go outside aes().Extend the same structure with new geoms, scales, and themes.
R version 4.4.1 (2024-06-14)
Platform: x86_64-conda-linux-gnu
Running under: Ubuntu 20.04.6 LTS
Matrix products: default
BLAS/LAPACK: /home/aabdallah/anaconda3/envs/R44/lib/libopenblasp-r0.3.30.so; LAPACK version 3.12.0
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=de_DE.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=de_DE.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=de_DE.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=C
time zone: Europe/Berlin
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggplot2_4.0.0
loaded via a namespace (and not attached):
[1] vctrs_0.6.5 nlme_3.1-168 cli_3.6.5 knitr_1.50
[5] rlang_1.1.6 xfun_0.52 generics_0.1.4 S7_0.2.0
[9] jsonlite_2.0.0 labeling_0.4.3 glue_1.8.0 htmltools_0.5.8.1
[13] scales_1.4.0 rmarkdown_2.29 grid_4.4.1 evaluate_1.0.4
[17] tibble_3.3.0 fastmap_1.2.0 yaml_2.3.10 lifecycle_1.0.4
[21] compiler_4.4.1 dplyr_1.1.4 RColorBrewer_1.1-3 pkgconfig_2.0.3
[25] mgcv_1.9-3 lattice_0.22-7 farver_2.1.2 digest_0.6.37
[29] R6_2.6.1 tidyselect_1.2.1 splines_4.4.1 pillar_1.10.2
[33] magrittr_2.0.3 Matrix_1.7-3 withr_3.0.2 tools_4.4.1
[37] gtable_0.3.6