In today’s class, we will make animated GIFs and videos from charts made in R with ggplot2, using the gganimate package.
Download the data for this session from here, unzip the folder and place it on your desktop. It contains the following folders and files:
nations.csv Data from the World Bank Indicators portal, as used in week 3 and subsequently.warming.csv National Oceanic and Atmospheric Administration data on the annual average global temperature, from 1880 to 2017.yearvalue Average global temperature, compared to average from 1900-2000.simulations.csv Data from NASA simulations of historical temperatures, estimating the effect of natural and human influences on climate, processed from the raw data used for this piece from Bloomberg News. Contains the following variables:yeartype Natural or Humanvalue Global average temperature from the simulation, relative to average simulated value from 1990-2000.Launch RStudio Cloud, upload the zipped folder with the data, and save a new R script as animations.R.
From the Packages panel, install the following packages: gganimate, transformr, gifski, png.
You can install them in one go, separated by spaces or commas. Make sure to have Install dependencies chacked.
# load required packages
library(readr)
library(ggplot2)
library(gganimate)
library(scales)
library(dplyr)
Apart from gganimate, we have encountered all of these packages in previous weeks.
The notes from the static charts class show how to make the following chart, showing GDP per capita, life expectancy at birth and population for the world’s nations in 2016:

This was the code to generate that chart:
# load data
nations <- read_csv("animations-r/nations.csv")
# make sure that year is treated as an integer
nations <- nations %>%
    mutate(year = as.integer(year))
# filter for 2016 data only
nations2016 <- nations %>%
  filter(year == 2016)
# make bubble chart
ggplot(nations2016, aes(x = gdp_percap, y = life_expect)) +
  xlab("GDP per capita") +
  ylab("Life expectancy at birth") +
  theme_minimal(base_size = 14, base_family = "Georgia") +
  geom_point(aes(size = population, color = region), alpha = 0.7) +
  scale_size_area(guide = FALSE, max_size = 15) +
  scale_x_continuous(labels = dollar) +
  stat_smooth(formula = y ~ log10(x), se = FALSE, size = 0.5, color = "black", linetype="dotted") +
  scale_color_brewer(name = "", palette = "Set2") +
  theme(legend.position=c(0.8,0.4))
Some reminders about what this code does:
scale_size_area ensures that the size of the circles scales by their area according to the population data, up to the specified max_size; guide = FALSE within the brackets of this function prevents a legend for size being drawn.
labels = dollar from scales formats the X axis labels as currency in dollars.
stat_smooth works like geom_smooth but allows you to use a formula to specify the type of curve to use for to trend line fitted to the data, here a logarithmic curve.
Now we will use gganimate to generate an animation of the chart, from 1990 to 2016. Here is the code:
# animate entire time series with gganimate
nations_plot <- ggplot(nations, aes(x = gdp_percap, y = life_expect)) +
  xlab("GDP per capita") +
  ylab("Life expectancy at birth") +
  theme_minimal(base_size = 14, base_family = "Georgia") +
  geom_point(aes(size = population, color = region), alpha = 0.7) +
  scale_size_area(guide = FALSE, max_size = 15) +
  scale_x_continuous(labels = dollar) +
  stat_smooth(formula = y ~ log10(x), se = FALSE, size = 0.5, color = "black", linetype="dotted") +
  scale_color_brewer(name = "", palette = "Set2") +
  theme(legend.position=c(0.8,0.4)) +
  # gganimate code
  ggtitle("{frame_time}") +
  transition_time(year) +
  ease_aes("linear") +
  enter_fade() +
  exit_fade()
Running this code will create an R object of type gganim called nations_plot.
Now display it in the Viewer panel by running the following:
animate(nations_plot)
transition_time This function animates the data by year, showing only the data that is relevant for any one point in time. As well as generating a frame for each year, it also generates intermediate frames to give a smooth animation."{frame_time}" within the ggtitle function puts a title on each frame with the corresponding value from the variable in the transition_time function, here year. (The earlier conversion of year to integer ensures that we don’t see decimal fractions in this title.)ease_aes This controls how the animation progresses. If animating over a time series, always use the option "linear" to ensure a constant speed for the animation. Other available options can be used when animating between different states of a chart, rather than over time, as we will see below.enter_fade exit_fade These functions control the behavior where a data point appears or disappears from the animation. You can also use enter_shrink and exit_shrink.We can now save the animation as a GIF or video.
# save as a GIF
animate(nations_plot, fps = 10, end_pause = 30, width = 750, height = 450)
anim_save("nations.gif")
# save as a video
animate(nations_plot, renderer = ffmpeg_renderer(), fps = 30, duration = 20, width = 800, height = 450)
anim_save("nations.mp4")
You can use the options width and height to set the dimensions, in pixels, of the animation; fps sets the frame rate, in frames per second; for a GIF, you can add a pause at the end using end_pause, here set to 30 frames or 3 seconds at 10 frames a second.
To make a video, you need the code renderer = ffmpeg_renderer(); duration sets the duration of the video. The video code above also sets the ratio between width and height at 16:9, consistent with YouTube format.
FFmpeg, a sofware library for working with video and audio, is already installed in RStudio Cloud. If working on your own computer, you will need to follow the instructions on the software page to install FFmpeg.
Here is the GIF:

And here is the video:
For the Gapminder-style video, we displayed only the data for the year in question in each frame. In some cases, however, you may want to animate by adding data with each frame, and leaving the previously added data in place.
We will explore that now by making animations similar the dot-and-line chart in this video.
Here is the code to make a static version of the chart:
# load data
warming <- read_csv("animations-r/warming.csv")
# draw chart
warming_plot <- ggplot(warming, aes(x = year, y = value)) +
  geom_line(colour="black") +
  geom_point(shape = 21, colour = "black", aes(fill = value), size=5, stroke=1) +
  scale_x_continuous(limits = c(1880,2017)) +
  scale_y_continuous(limits = c(-0.5,1)) +
  scale_fill_distiller(palette = "RdYlBu", limits = c(-1,1), guide = FALSE) +
  xlab("") +
  ylab("Difference from 1900-2000 (ºC)") +
  theme_minimal(base_size = 16, base_family = "Georgia")
This should be the result:

The file warming.csv contains the fields year and value, the latter being the global annual average temperature, compared to the 1900-2000 average.
As this is a dot-and-line chart, it includes both geom_line and geom_point layers. Notice that the geom_point function also defines a numbered shape: 21 is a circle with a filled area, see here for other options. By using this shape, we can set the outline color to black and then use an aes mapping to fill it with color, according to the temperature value.
The code uses scale_fill_distiller to use a ColorBrewer palette running from cool blues, through neutral yellows, to warm reds, applying them across a range of values from -1 to +1.
Again we can animate this data using gganimate:
# draw chart
warming_plot <- ggplot(warming, aes(x = year, y = value)) +
  geom_line(colour = "black") +
  geom_point(shape = 21, colour = "black", aes(fill = value), size = 5, stroke = 1) +
  scale_x_continuous(limits = c(1880,2017)) +
  scale_y_continuous(limits = c(-0.5,1)) +
  scale_fill_distiller(palette = "RdYlBu", limits = c(-1,1), guide = FALSE) +
  xlab("") +
  ylab("Difference from 1900-2000 (ºC)") +
  theme_minimal(base_size = 16, base_family = "Georgia") +
  # gganimate code
  transition_reveal(id = 1, along = year)
# save as a GIF
animate(warming_plot, fps = 10, end_pause = 30, width = 750, height = 450)
anim_save("warming.gif")
transition_reveal. This keeps the previously revealed data in place as each value for the along time variable is added to the chart.The default behavior for transition_reveal however, reveals the lines, but only plots the point for the current frame:

To create a cumulative animation of points, use code like this:
# draw chart
warming_points <- ggplot(warming, aes(x = year, y = value)) +
  geom_point(shape = 21, colour = "black", aes(fill = value), size=5, stroke=1) +
  scale_x_continuous(limits = c(1880,2017)) +
  scale_y_continuous(limits = c(-0.5,1)) +
  scale_fill_distiller(palette = "RdYlBu", limits = c(-1,1), guide = FALSE) +
  xlab("") +
  ylab("Difference from 1900-2000 (ºC)") +
  theme_minimal(base_size = 16, base_family = "Georgia") +
  # gganimate code
  transition_time(year) +
  shadow_mark()
# save as a GIF
animate(warming_points, fps = 10, end_pause = 30, width = 750, height = 450)
anim_save("warming_points.gif")
shadow_mark retains the data from previous frames.

Looped animations can also be used to switch between different states, or filtered views of the data. To illustrate this we will load the NASA data showing a simulation from climate models of how the global average temperature would have changed under the influence of natural events, such as variation in radiation from the Sun and the cooling effect of soot from volcanoes, compared to human influences, mostly emissions of carbon dioxide and other greenhouse gases.
This code will load the data and make the animation:
# load data
simulations <- read_csv("animations-r/simulations.csv")
# draw chart
simulations_plot <- ggplot(simulations, aes(x=year, y=value, color = value)) +
  geom_line(size = 1) +
  scale_y_continuous(limits = c(-0.6,0.75)) +
  scale_colour_distiller(palette = "RdYlBu", limits = c(-1,1), guide = FALSE) +
  ylab("Diff. from 1900-2000 average (ºC)") +
  xlab("") +
  theme_dark(base_size = 16, base_family = "Georgia") +
  #gganimate code
  ggtitle("{closest_state}") +
  transition_states(
    type,
    transition_length = 0.5,
    state_length = 2
  ) +
  ease_aes("sine-in-out")
# save as a GIF
animate(simulations_plot, fps = 10, width = 750, height = 450)
anim_save("simulations.gif")
transition_state. This switches between different filtered states of the data, here defined by the variable type. transition_length is the length of the transition in seconds, and state_length is the pause at each state, again in seconds.
ease_aes With a state transition animation, using options that  vary the speed of the transition, with a slower start and finish than the middle section, give a more visually pleaseing effect. Try "cubic-in-out" or "sine_in_out"
"{closest_state}" in the ggtitle function displays the appropriate value for the variable used to define the states, here type.The GIF should look like this:
