Animations

Claus O. Wilke

2025-04-16

Animations can create more engaging displays

 

Data source: World Bank

Animations can create more engaging displays

 

Data source: World Bank

We make animations in R with gganimate

library(tidyverse)
library(gganimate)

# make fun animations
...

The gganimate package adds powerful animation tools to ggplot2

Getting the data

We’ll be working with the gdp_ranked dataset:

gdp_ranked <- read_csv("https://wilkelab.org/SDS366/datasets/gdp_ranked.csv") |>
  mutate(rank = fct_rev(factor(rank)))

gdp_ranked
# A tibble: 500 × 6
   country country_code  year   gdp rank  gdp_rel
   <chr>   <chr>        <dbl> <dbl> <fct>   <dbl>
 1 Brazil  BRA           1970  42.3 10     0.0395
 2 Brazil  BRA           1971  49.2 10     0.0424
 3 Brazil  BRA           1972  58.5 10     0.0457
 4 Brazil  BRA           1973  79.3 10     0.0555
 5 Brazil  BRA           1974 105   9      0.0677
 6 Brazil  BRA           1975 124   9      0.0738
 7 Brazil  BRA           1976 153   9      0.0818
 8 Brazil  BRA           1977 176   8      0.0846
 9 Brazil  BRA           1978 201   8      0.0855
10 Brazil  BRA           1979 225   8      0.0856
# ℹ 490 more rows

How should we think about making an animation?

Think of an animation as faceting by time

 

We know how to make a faceted plot

gdp_ranked |>
  filter(year > 1985 & year %% 5 == 0) |>
  ggplot(aes(gdp, rank)) +
  geom_col(aes(fill = country)) +
  facet_wrap(~year)

 

Making an animation is about as complicated

gdp_ranked |>
  # gganimate uses the `group` aesthetic to track objects across frames
  ggplot(aes(gdp, rank, group = country)) + 
  geom_col(aes(fill = country)) +
  transition_states(year, transition_length = 5)

 

Adding country names and plot title

gdp_ranked |>
  ggplot(aes(gdp, rank, group = country)) +
  geom_col(aes(fill = country)) +
  geom_text(
    aes(x = -200, label = country),
    hjust = 1, size = 14/.pt
  ) +
  xlim(-7000, 23000) +
  labs(title = "year: {closest_state}") +
  theme_minimal_vgrid(14, rel_small = 1) +
  theme(
    axis.text.y = element_blank(),
    axis.title.y = element_blank(),
    axis.ticks.y = element_blank(),
    axis.line.y = element_blank()
  ) + 
  guides(fill = "none") +
  transition_states(year, transition_length = 5)

We make time series with transition_reveal()

selected <- c("China", "Japan",
  "United States", "Germany", "Brazil")

gdp_ranked |>
  filter(country %in% selected) |>
  ggplot(aes(year, gdp, color = country)) +
  geom_line() +
  geom_point(size = 3) +
  scale_y_log10() +
  transition_reveal(year)

This works also with ggrepel for labeling

gdp_ranked |>
  filter(country %in% selected) |>
  ggplot(aes(year, gdp, color = country)) +
  geom_line() +
  geom_point(size = 3) +
  geom_text_repel(
    aes(label = country),
    hjust = 0,
    nudge_x = 2,
    direction = "y",
    xlim = c(NA, Inf),
    segment.color = NA
  ) +
  scale_y_log10() +
  guides(color = "none") +
  coord_cartesian(clip = "off") +
  theme(plot.margin = margin(7, 100, 7, 7)) +
  transition_reveal(year)

Reproducing the famous gapminder animation

library(gapminder)

gapminder %>% filter(continent != "Oceania") |>
  ggplot() +
  aes(gdpPercap, lifeExp, size = pop) +
  geom_point(alpha = 0.7, color = "#0072B2") +
  scale_size(range = c(2, 12), guide = "none") +
  scale_x_log10(name = "GDP per capita") +
  facet_wrap(~continent, nrow = 2) +
  labs(
    title = "Year: {frame_time}",
    y = "life expectancy"
  ) +
  transition_time(year) +
  ease_aes("linear")

See Hans Rosling video here

Further reading