Last week I saw this post on Twitter where Adam Spannbauer visualized an animated ‘growing’ bar graph in R. Pretty impressive!


ASpannbauer_growing_barchart


Is an animated graph contributing to a better understanding?

From the comments and a related second post it became clear, that this animation was visually appealing, but in no way contributing to a better understanding of the data behind the graph. So I sat down and thought about requirements for an animated bar chart that can be embedded in a presentation and contribute (maybe) to a better understanding:

  • all bars should start simultaneously from the bottom and grow to the top with the same speed
  • when a sample reaches its maximum value it should stop growing
  • its maximum values should be displayed until plot stops
  • Carefully consider use of color (gradient colors)
  • Do not animate every single bar chart, listeners are not used to it and will be confused rapidly

In general, animations should be used very wisely, but can be a great option for telling a story.

Toy example of a possible solution

I will guide you trough the steps necessary to generate an animated bar graph in R. Down here you can find the R code, which you can also copy from my Github repository. I should mention that I only had to modify a few lines from @SPannbauers code.

# 1. step: Load all packages we need ----------------------------------------------------------------------------

library(data.table)     #order/add/modify/delete data
library(gganimate)      #create easy animations with ggplot2
library(RColorBrewer)   #Provides color schemes
library(ggplot2)        #A system for 'declaratively' creating graphics
# 2. step: Toy example ------------------------------------------------------------------------------------------

#toy example data
dt = data.table(time=1:10, x=round(runif(10, 50, 100), 0))

#number of frames per bar during animation
n_frames_per_bar = 33

#split dataset (e.g. every sample displayed on x-axis)
split_dt = split(dt, dt$time)

#split every sample (from 1 to maximum, in n_frames_per_bar steps)
split_dt_fill = lapply(split_dt, function(dti) {
  data.table(time=dti$time, x=seq(1, dti$x, by = max(dt$x)/n_frames_per_bar))
})

#every sample now has the same number of datapoints (absent datapoints are filled with max.value)
split_dt_fill2 <- lapply(split_dt_fill, function(dti){
  if (nrow(dti) < n_frames_per_bar){
    add_table <- data.table(time = dti$time[1], x = max(dti$x))
    dti <- rbind(dti, as.data.frame(lapply(add_table,function(x)rep(x,n_frames_per_bar - nrow(dti)))))
  }
  else{
    dti <- dti
  }
})

#bind together all the different samples (or 'time' points)
dt_fill = rbindlist(split_dt_fill2)

#id each row as its own frame for every 'time' | add column 'frame'
dt_fill[,frame := rep(1:n_frames_per_bar,10)]

#plot using ggplot2 (gganimate's frame arg in aes)
p = ggplot(dt_fill, aes(x=time, y=x, frame=frame, fill = x)) +
  geom_bar(stat="identity", position = "identity") +
  guides(fill=FALSE) +
  scale_fill_gradientn(colours = brewer.pal(9, "Blues")) +
  labs(x="Time", y="Value", title="")

#create gif (using interval to specify the time per frame)
gganimate(p, title_frame = FALSE, interval = .001)



barchart

I hope that this kind of animation is best suited for highlighting unexpected outliers in a presentation. And of course, it is very easy to play around with animation speed or changing colors to transport the story you want to tell.