Frame-blending is a great way to illustrate animal behaviour and other things that change over time. This got me thinking about ways to animate time series data. In R, the animation package has lots of options, but you can also build your own just by plotting over the same device window. If you save each iteration in a loop, the resulting images can be used as frames in a video or gif.

Hummingbirds deviate away from vertical stripe patterns

 Click the image to see a larger version

Here is an example using recordings that track hummingbirds flying in our tunnel here at UBC. This animation shows a bird’s eye view of 50 flights by 10 birds. In half of the flights (the red ones), the birds had horizontal stripes on their left side and vertical stripes on their right, and the other half (blue) had the reverse. The subtle difference between the red and blue trajectories (red ones tend to have more positive y values) shows that on average, birds tend to deviate away from vertical stripes, and towards horizontal ones. The histogram that builds up on the right side of the figure shows the mean lateral (y) position for each trajectory as it finishe

There are a couple of issues with this version: first, I had to cut it off before all of the flights were complete because the free gif maker I used at gifmaker.me only goes up to 100 frames. That is why the histogram ends up with a few gaps. The other issue is that the x and y axes are scaled very differently (i.e., the tunnel is not to scale).

By animating this dataset, you can see that birds vary a lot in speed, and crucially, that the difference between the red and blue groups increased for birds that progressed more slowly down the tunnel (i.e., the fast birds that get to the end first show less of a red/blue difference, whereas the slower ones tend to be further away from the midline at y=0).

Here’s an example of how to animate within R:

# first generate the data
data <- list()
for(i in 1:10){
length <- round(80-rnorm(1,30,10))
x <- sample(1:20,1):length
y <- rep(c(-4:5)[i], length(x))
data[[i]] <- data.frame(x=x, y=y)
}
lapply(data, 'head')
# function that we'll use to add points to the plot
myfun2 = function(x, index, col1 = 'red', cex1=0.5){
points(x$x[index], x$y[index], pch=16, cex=cex1, col=col1)
}
# set up the device window and animate
# a semi-transparent polygon gets added each step to make older points fade out
dev.new(width=11, height=4)
par(las=1, tck=0.015, bty='n', family='Times', mar=c(4,4,1,0))
plot(-10:10, xlim=c(0,100), ylim=c(-10,10), type='n', ylab='y', xlab='x')
for(i in seq(from=6,to=80)){
lapply(data, FUN=myfun2, index=i, col1=rgb(0,0,1,1))
polygon(x=c(0,100,100,0), y=c(-10,-10,10,10), col=rgb(1,1,1,0.25), border=NA)
}

If you wanted to save each step as a frame to be built into an animation outside of R, change the final chunk to something like this. This will generate 75 small jpgs in your working directory:
myfun3 = function(x, index, col1 = 'red', cex1=0.5){
points(x$x[index], x$y[index], pch=16, cex=cex1, col=col1)
}
for(i in seq(from=6,to=80)){
mypath <- file.path(getwd(), paste("myplot", i, ".jpg", sep = ""))
jpeg(file=mypath, width=11, height=4, units='in', res=150)
par(las=1, tck=0.015, bty='n', family='Times', mar=c(4,4,1,0))
plot(-10:10, xlim=c(0,100), ylim=c(-10,10), type='n', ylab='y', xlab='x')
lapply(data, FUN=myfun3, index=i-4, col1=rgb(0,0,1,1))
polygon(x=c(0,100,100,0), y=c(-10,-10,10,10), col=rgb(1,1,1,0.25), border=NA)
lapply(data, FUN=myfun3, index=i-3, col1=rgb(0,0,1,1))
polygon(x=c(0,100,100,0), y=c(-10,-10,10,10), col=rgb(1,1,1,0.25), border=NA)
lapply(data, FUN=myfun3, index=i-2, col1=rgb(0,0,1,1))
polygon(x=c(0,100,100,0), y=c(-10,-10,10,10), col=rgb(1,1,1,0.25), border=NA)
lapply(data, FUN=myfun3, index=i-1, col1=rgb(0,0,1,1))
polygon(x=c(0,100,100,0), y=c(-10,-10,10,10), col=rgb(1,1,1,0.25), border=NA)
lapply(data, FUN=myfun3, index=i, col1=rgb(0,0,1,1))
dev.off()
}