pak::pak(c("patchwork", "plotly", "ggiraph", "gganimate", "ggreveal"))16 Additional Topics in Data Visualization
- Combine multiple plots using the awesome patchwork package.
- Make interactive plots to let your audience dive into your data.
- Create animated visualizations to clearly show patterns and changes over time.
If you haven’t installed the following packages, run this code:
You’ve done great exploring data visualization so far. Now, let’s dive into some exciting extras that’ll really make your visualizations stand out—perfect for your papers, presentations, or blog posts!
16.1 Combining Plots with patchwork
Did you know that most academic journals limit you to about 6 figures per paper? To squeeze more information into fewer figures, you can combine multiple plots into one tidy visual (just a random exmaple I found online). The easiest and most intuitive way to do this is with the patchwork package—seriously, once you try it, you’ll never go back!
library(tidyverse)
library(patchwork)
library(palmerpenguins)
# First plot: Scatter plot
p1 <- ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
geom_point(alpha = 0.7) +
theme_minimal()
# Second plot: Density plot
p2 <- ggplot(penguins, aes(x = flipper_length_mm, fill = species)) +
geom_density(alpha = 0.5) +
theme_minimal()Use the + operator to combine plots horizontally:
p1 + p2
Use the / operator to combine plots vertically:
p1 / p2
Combine multiple plots in a single line of code:
p3 <- ggplot(penguins, aes(x = species, y = body_mass_g)) +
geom_boxplot(aes(fill = species)) +
labs(title = "Body Mass by Species") +
theme_minimal()
(p1 | (p2 / p3))- 1
- Complex layout: p1 on top, p2 and p3 side-by-side below

Let’s explore the advanced features of patchwork with one practical example:
p1 +
(p2 + guides(fill = "none")) +
plot_layout(
widths = c(5, 2)
) +
plot_layout(guides = "collect") +
plot_annotation(
tag_levels = "A"
) &
hrbrthemes::theme_ipsum()- 1
- Remove the legend from the first plot
- 2
- Left plot takes up 1/3 of the width
- 3
- Add a title to the entire composition
- 4
- Use Roman numerals for panel labels
- 5
-
Using
&as this applies the theme to the entire composition
16.2 Creating Interactive Plots
Unfortunately, interactive plots can’t be included in traditional academic papers (bummer!), but they’re incredibly effective for presentations, blogs, or web-based content, letting viewers actively explore your data.
16.2.1 plotly
Plotly offers the easist way to turn a ggplot2 static figure to an interactive version. Just plot as alwas, then use ggplotly() to convert it to an interactive plot.
library(plotly)
# Create basic ggplot as usual
p_plotly <- penguins |>
ggplot(aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
geom_point(
aes(text = paste(
"Species:", species,
"<br>Island:", island,
"<br>Body mass:", body_mass_g, "g"
)),
alpha = 0.7
) +
theme_minimal()
# Convert to plotly
ggplotly(p_plotly, tooltip = "text")- 1
-
is used to create a line break in the tooltip
16.2.2 ggiraph
While plotly gives you interactivity for free, ggiraph gives you interactivity with control. It is the tool of choice when you need precise customization — custom hover styles, linked highlighting across plots, and seamless integration with Quarto documents.
The key idea is simple: you swap standard ggplot2 geoms for their interactive counterparts (e.g., geom_point() → geom_point_interactive()), and then render the result with girafe() instead of printing it directly.
Every interactive geom in ggiraph understands two special aesthetics:
| Aesthetic | What it does |
|---|---|
tooltip |
The text that pops up when you hover over an element. Supports HTML (e.g., <b>, <br>). |
data_id |
A linking key. All elements that share the same data_id light up together on hover. |
Think of tooltip as the “what does this point say?” and data_id as the “what group does this point belong to?”
16.2.2.1 Basic Example: Tooltips + Linked Highlighting
Let’s build an interactive scatter plot. When you hover over any Adélie penguin, all Adélie penguins light up — and every other species fades out:
library(ggiraph)
p_ggiraph <- penguins |>
drop_na() |>
ggplot(aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
geom_point_interactive(
aes(
tooltip = paste(
"<b>Species:</b>", species,
"<br><b>Bill length:</b>", bill_length_mm, "mm",
"<br><b>Bill depth:</b>", bill_depth_mm, "mm"
),
data_id = species
),
size = 3, alpha = 0.7
) +
theme_minimal()
girafe(
ggobj = p_ggiraph,
options = list(
opts_hover(css = "fill:black;stroke:black;r:5pt;"),
opts_hover_inv(css = "opacity:0.1;"),
opts_zoom(max = 5)
)
) - 1
-
data_id = specieslinks all points of the same species — hover one, highlight them all. - 2
-
opts_hover()styles the highlighted elements (here: turn black, grow to 5pt radius). - 3
-
opts_hover_inv()styles everything else (here: fade to 10% opacity). - 4
-
opts_zoom()enables mouse-wheel zooming, up to 5Ă—.
The opts_hover() and opts_hover_inv() functions accept any valid CSS. Some useful patterns:
- Enlarge on hover:
"r:8pt;"(for points) or"stroke-width:3px;"(for lines) - Change color:
"fill:gold;stroke:orange;" - Dim the rest:
"opacity:0.05;"(very faint) or"filter:grayscale(100%);"(greyed out)
16.2.2.2 Linked Brushing Across Multiple Plots
Here’s where ggiraph truly shines. When you combine multiple plots with patchwork and give them the same data_id, hovering in one plot instantly highlights the matching data in all the others. This is called linked brushing, and it’s incredibly powerful for exploring multivariate data.
Try it below — hover over a species cluster in the left panel and watch the right panel respond:
library(patchwork)
# Left panel: Bill morphology
p1 <- penguins |>
drop_na() |>
ggplot(aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
geom_point_interactive(
aes(
tooltip = paste("Species:", species),
data_id = species
),
size = 2.5, alpha = 0.6
) +
labs(x = "Bill length (mm)", y = "Bill depth (mm)") +
theme_minimal() +
theme(legend.position = "none")
# Right panel: Body size
p2 <- penguins |>
drop_na() |>
ggplot(aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point_interactive(
aes(
tooltip = paste("Mass:", body_mass_g, "g"),
data_id = species
),
size = 2.5, alpha = 0.6
) +
labs(x = "Flipper length (mm)", y = "Body mass (g)") +
theme_minimal()
girafe(
ggobj = p1 + p2,
width_svg = 10, height_svg = 4,
options = list(
opts_hover(css = "opacity:1;"),
opts_hover_inv(css = "opacity:0.1;")
)
)- 1
-
The same
data_id = speciesappears in both plots — this is what links them. - 2
-
patchwork’s+operator combines the plots;girafe()renders them as a single interactive SVG.
dygraphs for time series
A great tool for interactive time series plots is the dygraphs package. It allows you to zoom in and out of the time series, highlight specific time periods, and display additional information on hover. It has a different syntax than ggplot2, but it’s worth learning if you work with time series data.
16.3 Animated Visualizations
Animations are awesome for illustrating changes over time or differences between groups. They’re engaging and clearly communicate your story, especially during presentations.
Smoothly animate plots over continuous changes (like time). The key new function is transition_reveal() which reveals data points, lines (or other geom) over time.
library(gganimate)
economics |>
ggplot(aes(x = date, y = unemploy)) +
geom_line(linewidth = 1, color = "#0072B2") +
geom_point(color = "white", fill = "#0072B2", shape = 21, size = 4) +
transition_reveal(date) +
labs(title = "Unemployment Rate Over Time") +
theme_minimal()- 1
- Transition the plot by revealing data points over time
To save the animation as a GIF, use the anim_save() function.
For discrete groups, sometimes it is easier to just use ggreveal. It creates a series of plots, showing how this plot is being built
library(ggreveal)
p <- penguins |>
filter(!is.na(sex)) |>
ggplot(aes(body_mass_g, bill_length_mm,
group=sex, color=sex)) +
geom_point() +
geom_smooth(method="lm", formula = 'y ~ x', linewidth=1) +
facet_wrap(~species) +
theme_minimal()
reveal_groups(p)