Animated Plot Tutorial

Todd R. Jones 2021-09-10 6 minute read

First load the necessary packages:

library(tidyverse)
library(ggplot2)
library(gganimate)
library(countrycode)
library(RColorBrewer)
library(gganimate)
#devtools::install_github("rensa/ggflags")
library(ggflags)
library(ggthemes)

We will use three files, all available from https://www.gapminder.org/data/:

  1. GDP—income_per_person_gdppercapita_ppp_inflation_adjusted.csv.
    • Click on the Income indicator.
  2. Child Mortality—child_mortality_0_5_year_olds_dying_per_1000_born.csv
    • Click on the Child Mortality indicator.
  3. Total Population—population_total.csv
    • Click on the Population indicator.

You can also download these files here.

If necessary, set your working directory to where you project is located.

setwd("~/")

Clean the data:

gdp_w <- read.csv("files/income_per_person_gdppercapita_ppp_inflation_adjusted.csv")
mort_w <- read.csv("files/child_mortality_0_5_year_olds_dying_per_1000_born.csv")
pop_w <- read.csv("files/population_total.csv")

#data begin in 1800. keep only 1900-2018
gdp_w <- gdp_w[,c(1, 102:218)]
mort_w <- mort_w[,c(1, 102:218)]
pop_w <- pop_w[,c(1, 102:218)]

#get data from wide to long
gdp <- gather(gdp_w, Year, gdp, starts_with("X"))
mort <- gather(mort_w, Year, mort, starts_with("X"))
pop <- gather(pop_w, Year, pop, starts_with("X"))

#remove X from beginning
gdp$Year<-substring(gdp$Year, 2)
mort$Year<-substring(mort$Year, 2)
pop$Year<-substring(pop$Year, 2)

#merge
data <- merge(gdp, mort, by=c("country", "Year"))
data <- merge(data, pop, by=c("country", "Year"))

#label year
names(data)[2] <- "year"

#create log gdp variable
data$lgdp <- log(data$gdp)

#convert year to integer
data$year <- as.integer(data$year)

#merge on continent
data$continent <- countrycode(sourcevar = data[, "country"], 
                              origin = "country.name", 
                              destination = "continent")

#make country 2 digit to work with ggflags
data$country2 <- countrycode(as.character(data$country),
                             "country.name","iso2c")
data$country2 <- tolower(data$country2)
data$country2 <- as.character(data$country2)

#don't consider Oceania
data <- data[data$continent!="Oceania",]
  1. Barebones. We will just plot the year 1900.
plot <- ggplot(data[data$year==1900,], aes(x=lgdp, y=mort)) +
  geom_point() + 
  ylim(0, NA)
#ggsave(plot, filename="output/plot1.png", width=10,height=5.5,units='in',dpi=200)
plot

Note that you can save the static image using something like:

ggsave(plot, filename="output/plot1.png", width=10,height=5.5,units='in',dpi=200)

A. Add axis labels.

plot <- ggplot(data[data$year==1900,], aes(x=lgdp, y=mort)) +
  geom_point() + 
  ylim(0, NA) +
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born")

plot

B. Change the labeling of the x-axis (and plot gdp, not lgdp)

plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point() + 
  ylim(0, NA) +
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma)

plot

C. Change the size of the dot to reflect the country’s population

plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop), show.legend=FALSE) + 
  scale_size_continuous(range = c(4, 25)) +  
  ylim(0, NA) +
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma)

plot

D. Change color of dots (to be the y-variable)

colourCount = length(unique(data$mort))
getPalette = colorRampPalette(brewer.pal(9, "Blues"))
plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop, col=mort), show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +  
  scale_size_continuous(range = c(4, 25)) +  
  ylim(0, NA) +
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma)

plot

E. Add opacity to the dots

plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop, col=mort), alpha=.7, show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +    
  scale_size_continuous(range = c(4, 25)) +  
  ylim(0, NA) +
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma)

plot

F. Change the background from grey to white

plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop, col=mort), alpha=.7, show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +    
  scale_size_continuous(range = c(4, 25)) +  
  ylim(0, NA) +
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma) +
  theme(panel.background = element_rect(fill = 'white', colour = 'white'))   

plot

G. Add in black axis lines

plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop, col=mort), alpha=.7, show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +    
  scale_size_continuous(range = c(4, 25)) +  
  ylim(0, NA) +
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma) +
  theme(panel.background = element_rect(fill = 'white', colour = 'white')) +
  theme(axis.line = element_line(size = .6, colour = "black"))   

plot

H. Make the y axis start right at 0

plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop, col=mort), alpha=.7, show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +    
  scale_size_continuous(range = c(4, 25)) +  
  expand_limits(y=0) +  
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma) +
  theme(panel.background = element_rect(fill = 'white', colour = 'white')) +
  theme(axis.line = element_line(size = .6, colour = "black"))+
  scale_y_continuous(expand = c(0, 0))

plot

I. Increase the size of the labels

plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop, col=mort), alpha=.7, show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +    
  scale_size_continuous(range = c(4, 25)) +  
  expand_limits(y=0) +  
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma) +
  theme(panel.background = element_rect(fill = 'white', colour = 'white')) +
  theme(axis.line = element_line(size = .6, colour = "black"))+
  scale_y_continuous(expand = c(0, 0)) +
  theme(plot.title = element_text(size=22)) +
  theme(plot.caption = element_text(size=16)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(plot.subtitle = element_text(size=16, hjust = 0.5, vjust=-10)) +
  theme(axis.text=element_text(size=16), axis.title=element_text(size=16))   

plot

J. Add captions

plot <- ggplot(data[data$year==1900,], aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop, col=mort), alpha=.7, show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +    
  scale_size_continuous(range = c(4, 25)) +  
  expand_limits(y=0) +  
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma) +
  theme(panel.background = element_rect(fill = 'white', colour = 'white')) +
  theme(axis.line = element_line(size = .6, colour = "black"))+
  scale_y_continuous(expand = c(0, 0)) +
  theme(plot.title = element_text(size=22)) +
  theme(plot.caption = element_text(size=16)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(plot.subtitle = element_text(size=16, hjust = 0.5, vjust=-10)) +
  theme(axis.text=element_text(size=16), axis.title=element_text(size=16)) +
  labs(caption = "By @toddrjones. Source: Gapminder. ")   

plot

K. Animate

plot <- ggplot(data, aes(x=gdp, y=mort)) +
  geom_point(aes(size=pop, col=mort), alpha=.7, show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +    
  scale_size_continuous(range = c(4, 25)) +  
  expand_limits(y=0) +  
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma) +
  theme(panel.background = element_rect(fill = 'white', colour = 'white')) +
  theme(axis.line = element_line(size = .6, colour = "black"))+
  scale_y_continuous(expand = c(0, 0)) +
  theme(plot.title = element_text(size=22)) +
  theme(plot.caption = element_text(size=16)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(plot.subtitle = element_text(size=16, hjust = 0.5, vjust=-13)) +
  theme(axis.text=element_text(size=16), axis.title=element_text(size=16)) +
  labs(caption = "By @toddrjones. Source: Gapminder. ")  +
  transition_time(year) +   
  labs(subtitle = paste('Year: {frame_time}'))  

plot

Note that you can save the static image using something like:

anim_save("output/plot1l.gif", plot2, end_pause=8, width = 600, height = 400, duration=11, nframes=220)

L. Split into continents using facet

plot2 <- plot + facet_wrap(. ~ continent, ncol=2) + theme(strip.text = element_text(size=17))
plot2

M. Change the dots to flags

plot <- ggplot(data, aes(x=gdp, y=mort)) +
  geom_flag(aes(size=pop, country=country2), show.legend=FALSE) + 
  scale_colour_gradientn(colours=rev(getPalette(1444)[700:1444])) +    
  scale_size_continuous(range = c(4, 25)) +  
  expand_limits(y=0) +  
  labs(x = "Log GDP Per Capita, PPP",
       y = "0-5 Year Old Deaths/1000 Born") +
  scale_x_log10(breaks=c(1000, 10000, 100000), label=scales::comma) +
  theme(panel.background = element_rect(fill = 'white', colour = 'white')) +
  theme(axis.line = element_line(size = .6, colour = "black"))+
  scale_y_continuous(expand = c(0, 0)) +
  theme(plot.title = element_text(size=22)) +
  theme(plot.caption = element_text(size=16)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(plot.subtitle = element_text(size=16, hjust = 0.5, vjust=-13)) +
  theme(axis.text=element_text(size=16), axis.title=element_text(size=16)) +
  labs(caption = "By @toddrjones. Source: Gapminder. ")  +
  transition_time(year) +   
  labs(subtitle = paste('Year: {frame_time}')) +
  facet_wrap(. ~ continent, ncol=2) + theme(strip.text = element_text(size=17))  

plot

N. Go away from animation and instead display using small multiples

plot <- ggplot(data[data$continent=="Europe",], aes(x=year, y=mort)) +
  geom_point(size=1, show.legend=FALSE) + 
  theme(panel.background = element_rect(fill = 'white', colour = 'white')) +
  theme(plot.title = element_text(size=22)) +
  theme(plot.caption = element_text(size=16)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(plot.subtitle = element_text(size=16, hjust = 0.5, vjust=-10)) +
  theme(axis.text=element_text(size=16), axis.title=element_text(size=16)) +    
  labs(x = "Year",
       y = "0-5 Year Old Deaths/1000 Born") +
  labs(caption = "By @toddrjones. Source: Gapminder. ") +
  ylim(0, NA) +
  facet_wrap(. ~ country) +
  scale_x_continuous(breaks=c(1900, 2000)) +
  theme(axis.text.x=element_text(size=rel(0.7))) +
  theme(axis.text.y=element_text(size=rel(0.7)))

plot