ggplot2 Extensions: patchwork, ggrepel, gganimate
ggplot2 is the most popular data visualization package in R, but its core functionality gets you only so far. The real power unlocks when you add extension packages from the ggplot2 library. Three extensions stand out for everyday use: patchwork for combining plots, ggrepel for readable labels, and gganimate for animations.
This guide shows you when and how to use each extension with practical examples.
Installing the Extensions
All three packages are available from CRAN:
install.packages(c("patchwork", "ggrepel", "gganimate"))
Load them alongside ggplot2:
library(ggplot2)
library(patchwork)
library(ggrepel)
library(gganimate)
ggplot2 Extensions: Combining Plots with patchwork
The patchwork package solves a common problem: how do you arrange multiple plots in one figure? Base R’s layout system is clunky, and gridExtra feels limited. patchwork integrates directly with ggplot2 using intuitive operators that feel natural.
The + Operator
The + operator places plots side by side in a row:
p1 <- ggplot(mtcars, aes(mpg, disp)) + geom_point()
p2 <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p1 + p2
This creates a two-column layout with p1 on the left and p2 on the right.
The / Operator
The / operator stacks plots vertically:
p1 / p2
This creates two rows—one plot on top, another below.
Complex Layouts
Combine operators for complex arrangements:
(p1 + p2) / p1
This creates two plots on top (side by side) and one plot spanning the full width below.
Controlling Layout
Use plot_layout() for fine control over dimensions:
p1 + p2 +
plot_layout(widths = c(1, 2))
This makes the second plot twice as wide as the first.
You can also control heights and add guides:
p1 + p2 +
plot_layout(heights = c(2, 1), guides = "collect")
The guides = "collect" option gathers legends from individual plots into one—useful when all plots share the same color mapping.
Adding Annotations Across Plots
Patchwork can add text annotations that span the entire figure:
p1 + p2 +
plot_annotation(
title = "Motor Trend Car Data",
subtitle = "Comparing displacement and weight to MPG",
caption = "Source: mtcars dataset"
)
This is much cleaner than manually adding text to each subplot.
Preventing Overlapping Labels with ggrepel
When you add text labels to a plot, they often overlap—especially with many points or long label text. ggrepel pushes labels away from each other and from the data points, keeping everything readable.
Basic Usage
Replace geom_text() with geom_text_repel():
ggplot(mtcars, aes(mpg, disp, label = rownames(mtcars))) +
geom_point() +
geom_text_repel()
The labels now have room to breathe. The algorithm finds positions that minimize overlaps while keeping each label near its data point.
Highlighting Selected Points
You rarely want to label every single point. Use the data argument to label only certain observations:
top_cars <- mtcars[order(mtcars$mpg, decreasing = TRUE)[1:5], ]
ggplot(mtcars, aes(mpg, disp)) +
geom_point() +
geom_text_repel(data = top_cars, aes(label = rownames(top_cars)))
This approach works well for highlighting outliers, top performers, or any subset worth emphasizing.
Styling the Labels
Control appearance through standard ggplot2 aesthetics:
ggplot(mtcars, aes(mpg, disp, label = rownames(mtcars))) +
geom_point() +
geom_text_repel(
color = "red",
size = 3,
fontface = "bold"
)
Using Boxes Instead of Text
For better visibility against busy backgrounds, use geom_label_repel() instead:
ggplot(mtcars, aes(mpg, disp, label = rownames(mtcars))) +
geom_point() +
geom_label_repel(
box.color = "darkgray",
fill = "white",
alpha = 0.8
)
Controlling Label Behavior
Fine-tune the algorithm with parameters:
geom_text_repel(
max.overlaps = 15, # Skip labels if too many
min.segment.length = 0, # Always draw segment
nudge_x = 0.5, # Offset from point
direction = "both" # Allow movement in any direction
)
Creating Animations with gganimate
Static plots show one moment in time. gganimate extends ggplot2 to show how your data changes across states—whether time periods, categories, or steps in an algorithm.
Your First Animation
Start with a static plot, then add transition functions:
ggplot(iris, aes(Petal.Length, Petal.Width, color = Species)) +
geom_point(size = 3) +
transition_states(Species, transition_length = 2, state_length = 1)
Each species appears sequentially, with points fading in and out as the animation progresses.
Animating Over Time
Use a dataset with a time variable:
# Sample time series data
set.seed(123)
dates <- seq(as.Date("2024-01-01"), by = "day", length.out = 30)
df <- data.frame(
date = dates,
value = cumsum(rnorm(30))
)
ggplot(df, aes(date, value)) +
geom_line() +
geom_point() +
transition_reveal(date)
The line draws itself as time progresses—the reveal animation traces from left to right.
Animation Easing
Control how values transition between states:
ggplot(iris, aes(Petal.Length, Petal.Width, color = Species)) +
geom_point(size = 3) +
transition_states(Species, transition_length = 2, state_length = 1) +
ease_aes("cubic-in-out")
Common options include linear, cubic, and elastic easing.
Saving Animations
Use anim_save() to export your work:
animated_plot <- ggplot(df, aes(date, value)) +
geom_line() +
transition_reveal(date)
anim_save("animation.gif", animation = animated_plot)
For higher quality, save as MP4:
anim_save("animation.mp4", animation = animated_plot, renderer = av_renderer())
Performance Tips
Animations can get heavy. Keep these tips in mind:
- Fewer frames render faster. Use
nframesto control this. - Complex geometries slow things down. Keep animations simple.
- Test with
print()before saving to catch errors quickly.
Conclusion
Each extension serves a different purpose in your visualization workflow:
- Use patchwork when you need to combine multiple plots into one figure. The operator syntax makes layouts intuitive, and the annotation features add professional polish.
- Use ggrepel whenever text labels crowd your visualization. It handles the hard work of positioning so you don’t have to manually adjust coordinates.
- Use gganimate to show change over time or across categories. Keep animations simple—too much motion confuses rather than informs.
All three ggplot2 extensions integrate seamlessly with ggplot2, so your existing knowledge transfers directly. Start with patchwork if you combine plots regularly; add ggrepel next time your labels overlap. Once you’re comfortable, gganimate opens up new ways to tell data stories with ggplot2 extensions.
See Also
- Introduction to ggplot2 — Foundation concepts before using extensions
- Customizing ggplot2 Charts — Themes, colors, and annotations
- Interactive Plots with plotly — For web-based interactivity instead of animations