# A quick demonstration of animated visualizations in R

A brief look at gganimate and plotly animations

Cynarina (Left) and Trachyphyllia (Right)

A rather famous TED Talk titled “The best stats you’ve ever seen” was given in February 2006 by Hans Rosling. Here he presented global economic, health, and development data from the website gapminder.org with rather striking visuals.

For this demonstration I’ll follow in Hans’ footsteps and also use the gapminder package which contains an excerpt of data from GapMinder.org examining country GDP from the year 1952 up to 2007.

The focus is going to be on building animations while utilizing the grammar of graphics. The two methods of forming these animations will be first with gganimate and second with plotly.

There will be two different renderers I will use with gganimate. The gifski renderer will produce a gif and the av renderer will produce a produce a video.

library(tidyverse)
library(gapminder)
library(gganimate)
library(plotly)

# ggplot2 styling
library(scales)
library(ggdark)

# These are used for rendering gganimate
# They do not need loaded
library(gifski)
library(av)
library(transformr)

# The package widgetframe is used to handle CSS conflicts with plotly
# https://github.com/rstudio/blogdown/issues/20
library(widgetframe)

Here we start by building the actual visualization as a simple ggplot. Something to note is gganimate::transition_time will supply the value of frame_time for string literal interpretation.

my_plot_ggplot <-
gapminder %>%
ggplot(mapping = aes(x = gdpPercap,
y = lifeExp,
color = continent)) +
geom_point(mapping = aes(size = pop),
alpha = 0.4) +
geom_smooth(method = "lm",
se = FALSE,
formula = y~x) +
scale_x_log10(labels = scales::label_comma()) +
labs(color = "Continent",
size = "Population",
y = "Life Expectancy",
x = "GDP per Capita") +
scale_size(labels = scales::label_comma()) +
labs(title = "Life Expectancy of a Country by GDP per Capita",
subtitle = "Year: {frame_time}",
caption = "Least Squares fit on Log10 transformed X",
tag = "GapMinder Data") +
ggdark::dark_theme_gray()
my_plot_ggplot

At this point we’re presented with a slightly jumbled graph. Here each point represents a given country at a given year. This means the same country is represented multiple times as there is a point for each year. The next step is to use gganimate which, in the background, will generate a series of plots representing a specific year and then combine them into an animation that transitions through the years. As I am using ggplot2::geom_smooth I’ll have to make use of transformr to update the regression lines for each year. This is fairly straightforward, and produces a rather impressive visualization with very little effort:

my_plot_gganimate <-
my_plot_ggplot +
transition_time(year)
animate(plot = my_plot_gganimate,
renderer = gifski_renderer(),
height = 800, width = 800, duration = 12)

my_plot_gganimate <-
my_plot_ggplot +
transition_time(year)
animate(plot = my_plot_gganimate,
renderer = av_renderer(),
height = 800, width = 800, duration = 12)

Another useful method is to take advantage of plotly. The graphing library offered by Plotly is fairly powerful in it’s own right. By knowing the grammar of graphics you can easily take advantage of some of plotly’s interactive features by using plotly::ggplotly and tweaking it from there. It terms of making a quick demo or deciding on whether the interactivity is the direction to go, this is incredibly useful.

Passing our original ggplot won’t quite give us the animation we desire, however.

my_plot_ggplot %>%
ggplotly() %>%
widgetframe::frameWidget()

To get the animated effect we have to assign the variable to be used as a frame in the ggplot aesthetic mapping. This is similar to gganimate’s old API.

my_plot_ggplotly <-
gapminder %>%
ggplot(mapping = aes(x = gdpPercap,
y = lifeExp,
color = continent,
frame = year)) +
geom_point(mapping = aes(size = pop),
alpha = 0.4) +
geom_smooth(method = "lm",
se = FALSE,
formula = y~x) +
scale_x_log10(labels = scales::label_comma()) +
labs(color = "Continent",
size = "Population",
y = "Life Expectancy",
x = "GDP per Capita") +
scale_size(labels = scales::label_comma()) +
labs(title = "Life Expectancy of a Country by GDP per Capita",
subtitle = "Year: {frame_time}",
caption = "Least Squares fit on Log10 transformed X",
tag = "GapMinder Data") +
ggdark::dark_theme_gray()
my_plot_ggplotly %>%
ggplotly() %>%
widgetframe::frameWidget()

This opens up a lot of room for creativity with the level of interactivity plotly introduces. For example, we could add one additional bit of information to the plot by identifying the what country each point represents and it would allow someone to track and identify specific countries.

my_plot_ggplotly2 <-
gapminder %>%
ggplot(mapping = aes(x = gdpPercap,
y = lifeExp,
color = continent,
frame = year,
ids = country)) +
geom_point(mapping = aes(size = pop),
alpha = 0.4) +
geom_smooth(mapping = aes(ids = NULL),
method = "lm",
se = FALSE,
formula = y~x) +
scale_x_log10(labels = scales::label_comma()) +
labs(color = "Continent",
size = "Population",
y = "Life Expectancy",
x = "GDP per Capita") +
scale_size(labels = scales::label_comma()) +
labs(title = "Life Expectancy of a Country by GDP per Capita",
subtitle = "Year: {frame_time}",
caption = "Least Squares fit on Log10 transformed X",
tag = "GapMinder Data") +
ggdark::dark_theme_gray()
my_plot_ggplotly2 %>%
ggplotly() %>%
widgetframe::frameWidget()

One could also build a plot using plotly’s own syntax, and the vast catalogue of example visualizations offered by plotly can be invaluable with learning it.