This code through explores how to use the gganimate package in R to create animated data visualizations. While a traditional graph shows us what the data looks like at one point or across an entire data set, an animated graph can help us see how the data changes over time.
To demonstrate this concept, we will create a fictional 30-game volleyball season involving six teams. We will use team hitting percentage and winning percentage to watch how each team’s performance changes throughout the season.
Specifically, we’ll explain and demonstrate how to take a basic ggplot2 visualization and turn it into an animation using gganimate. We will creat fictional volleyball data, explore the data, create a static graph, and then add animation to. show the season progressing from Game 1 through Game 30.
This topic is valuable because data often changes over time, and those changes can be difficult to see in a single static graph. For example, two volleyball teams may finish a season with similar records but take very different paths to get there. Animation allows us to see those changes as they happen.
The same technique can also be applied to areas such as education, public health, human services, business, and other field where outcomes are measured over time.
Specifically, you’ll learn how to:
Here, we will create a fictional volleyball dataset and use it to demonstrate how gganimate can help us visualize changes throughout a season.
gganimate extends ggplot2 by adding movement to data visualizations. Instead of showing every observation at once, we can tell R how the graph should change as time passes.
For this example, each team will have 30 observations representing Games 1 through 30. We will use game as our time variable. This allows gganimate to show us how team performance changes throughout the season.
The basic process will be: Create the data -> Make a static graph -> Add animation -> Interpret the results
A basic example shows how to create a fictional volleyball dataset. We will use six teams with different performance patterns to demonstrate how their seasons might unfold.
# Make the fictional data reproducible
set.seed(123)
# Create the teams
teams <- c("Falcons", "Panthers", "Wolves", "Tigers", "Hawks", "Lions")
# Create 30 games for each team
volleyball_data <- expand.grid(
game = 1:30,
team = teams
) %>%
arrange(team, game) %>%
mutate(
# Add small random variations
hitting_noise = rnorm(n(), 0, 0.008),
winning_noise = rnorm(n(), 0, 0.035),
# Create different performance patterns
hitting_pct = case_when(
team == "Falcons" ~ 0.145 + game * 0.004,
team == "Panthers" ~ 0.245,
team == "Wolves" ~ 0.275,
team == "Tigers" ~ 0.120 + game * 0.005,
team == "Hawks" ~ 0.195,
team == "Lions" ~ 0.110
) + hitting_noise,
win_pct = case_when(
team == "Falcons" ~ 0.250 + game * 0.018,
team == "Panthers" ~ 0.700,
team == "Wolves" ~ 0.400 + sin(game * 1.2) * 0.150,
team == "Tigers" ~ 0.100 + game * 0.018,
team == "Hawks" ~ 0.480,
team == "Lions" ~ 0.180
) + winning_noise
) %>%
mutate(
hitting_pct = pmin(pmax(hitting_pct, 0.05), 0.40),
win_pct = pmin(pmax(win_pct, 0.05), 0.95)
)
# Display the first six rows
head(volleyball_data)The expand.grid() function creates every combination of the six teams and 30 games, givign us 180 observations.
The mutate() function creates new variables, while case_when() allows us to assign different performance patterns to each team. For example, the Falcons’ hitting percentage increases throughout the season, while the Panthers’ hitting percentage remains relatively stable.
The set.seed() function makes the random variations reproducible so that the same fictional data can be generated again.
We can check the size of our dataset with :
## [1] 180 6
The result should be 180 rows and 6 columns. Each row represents one team’s performance during one game.
More specifically, we can use ggplot2 to create a static graph comparing hitting percentage and winning percentage.
# Create a static scatterplot
ggplot(
volleyball_data,
aes(
x = hitting_pct,
y = win_pct,
color = team,
group = team
)
) +
geom_point(size = 3) +
labs(
title = "Volleyball Team Performance",
x = "Hitting Percentage",
y = "Winning Percentage",
color = "Team"
) +
theme_minimal()The ggplot() function identifies the dataset and the variables used in the graph. The aes() function determines where the variables appear: hitting percentage is on the x-axis, winning percentage is on the y-axis, and color distinguishes the teams. The geom_point() function displays the observations as points.
This static graph allows us to compare team performance, but it does not clearly show how each team’s performance changes from game to game.
What’s more, gganimate allows us to add movement to the graph using transition_time().
# Animate performance throughout the season
volleyball_animation <- ggplot(
volleyball_data,
aes(
x = hitting_pct,
y = win_pct,
color = team,
group = team
)
) +
geom_point(size = 4) +
labs(
title = "Volleyball Season: Game {frame_time}",
x = "Hitting Percentage",
y = "Winning Percentage",
color = "Team"
) +
theme_minimal() +
transition_time(game)
animate(
volleyball_animation,
renderer = gifski_renderer()
)
# Display the animation
volleyball_animationThe important addition is transition_time(game). This tells gganimate to use the game number as the time dimension, allowing the points to move as the season progresses.
The title also includes {frame_time}, which updates to display the current game number.
Most notably, we can use shadow_wake() to show a trail behind each team’s current position.
The shadow_wake() function displays recent frames behind the current frame. This creates a trail that helps us follow each team’s movement throughout the animation.
For example, the Falcons should generally move toward higher hitting and winning percentages as the season progresses. The Panthers should remain relatively stable, while the Wolves should show more variation in winning percentage.
These patterns demonstrate how animation can help us see changes that might be less obvious in a static graph.
The animation allows us to explore several questions:
Because the dataset is fictional, these patterns demonstrate how the technique works rather than providing evidence about actual volleyball teams.
The example also illustrates an important data science principle: a relationship between two variables does not necessarily mean that one causes the other. Additional data and analysis would be needed to explain why teams perform differently.
Learn more about gganimate and data visualization with the following:
gganimate Hyperlink Text
gganimate Hyperlink Text
Data Visualization Hyperlink Text
This code through references and cites the following sources:
Pedersen, T. L., Robinson, D., & Posit, PBC. (n.d). gganimate: A Grammar of Animated Graphics. Hyperlink Text
Pedersen, T. L., Robinson, D., & Posit, PBC. (n.d.). gganimate Reference Documentation. Hyperlink Text
Wickham, H., Chang, W., Henry, L., Pedersen, T. L., Takahashi, K., Wilke, C., Woo, K., Dunnington, D., & Yutani, H. (n.d.). ggplot2: Create Elegant Data Visualisations Using the Grammar of Graphics. Hyperlink Text