Animation in R
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Information about the student and the project.
- Contributed by Malcolm Hess
- Malcolm attended the pioneer class of the NYCdata Science Academy bootcamp with instructor Vivian Zhang.
- This post is based on his research into creating animations in R.
Slides/Videos.
Basic animation in R
About the project.
-Background
The purpose of this project is to look into the animation package in R. Animated graphs can adds a unique element to data visualization which may provide greater detail than a static graph. It also serves as a great way to improve the aesthetics of a presentation or report which will help retain a viewer’s attention.
-Goal
The goal is to create an animated graph in R. I also wanted to create an animation with a transparent background so it can potentially be used as a graphic in a video.
Process
Install the package.
install.packages(“animation”)
This package creates animations in either an HTML, Latex, SWF, Video, or GIF format. However to save images in GIF format you will need to install ImageMagick. The ImageMagick site is not very friendly and installing it yourself can be a pain. I suggest you use HomeBrew for a quick and easy installation. {after HomeBrew is installed apply this command from console/terminal}
brew install imagemagick
There can be significant issues with ImageMagic on the Windows OS. One type of problem people is that the command to use ImageMagick to convert images to GIFs is “convert”, which Windows already has allocated to another function. So when you run the convert command it pulls up the original function call and not ImageMagick. I myself encountered a different issue where accessing ImageMagic from my console worked fine but it did not work when accessing the console through R. Creating GIFs in R is a finicky process because it has to go through the unreliable ImageMagic. All of my code examples here are for creating GIFs, however most of the code is easily converted to HTML by simply changing the function call and the file extension name.
What is an animation?
The most important idea to understand behind animation is that is is essentially a series of static pictures. Creating an animated graph works the exact same way. To do this you create the completed graph and then limit the data flow that it can use to plot. You then iterate through all of the data starting from the beginning to end (or all of the data points if you are animating a line). The saveGIF command will take all the plots it has seen and then compact them together into a GIF.
saveGIF({
ani.options(interval = 0.2, nmax = 50)
t = seq(0,pi,.01)
x = cos(2*t)
y = sin(2*t)
idx = seq(1,length(x),10)
for (i in seq_along(idx)) {
plot(x,y,type=’n’)
points(x[seq(idx[i])],
y[seq(idx[i])], pch=15, col=’dark green’)
ani.pause() }
}, movie.name = “circle.gif”,
ani.width = 600, ani.height = 600)
Looking at this example, there are a few important parameters to notice. The interval parameter inside of ani.options is the time between frames. The nmax parameter is the number of steps in a loop (e.g. iterations to create animation frames. The default is 50, I’ve found that if you try to change it seems to always throw up a warning and tell you it reset it to 50. You will notice that inside of the saveGIF command there is a loop that cycles through the length of the data which will creates all of the images. After each image the ani.pause() function is used to create the delay between frames. The ani.pause() function is the interval parameter which is set in the ani.options.
As you can see the results of the a saveGIF function don’t always work. Your GIFs might have blank frames as this circle GIF does, GIFs can have pure black frames, and it can even have strange ordering where the frames effectively go from image 19 to image 2, then back up to image 20.
t = seq(0,1.1*pi,.01)
x = cos(2*t) * sin(2*t)
y = tan(2*t) * cos(2*t)
while (i <= length(x)) {
frame = 100 + i
filename <- paste("test", frame, ".png", sep="")
png(file=filename, width=550, height=550)
plot(x,y,type='n')
points(x[1:i], y[1:i], pch=15,col='dark green')
i <- i+1
dev.off() }
system("convert -delay 40 *.png example_1.gif")
There is more than one way to make a GIF. Instead of calling the saveGIF command, you can save all of the individual images to the disk. Then through the system command you tell ImageMagick to convert the PNG files into a GIF.
maxrows <- nrow(cardata) - 4
i<- 1
saveGIF({
ani.options(interval = 0.1, nmax=maxrows)
while(i<maxrows) {
g <- ggplot(cardata, aes(x[i],y[i]), alpha = .05) +
xlim(xlimits) + ylim(ylimits) +
geom_point(colour = "tan", size = 2)
m <- geom_point(data=cardata, aes(x[(i+1)],y[(i+1)]),
colour = "tan", size = 3, alpha = .05)
n <- geom_point(data=cardata, aes(x[(i+2)],y[(i+2)]),colour = "tan", size = 4, alpha = .1)
o <- geom_point(data=cardata, aes(x[(i+3)],y[(i+3)]), colour = "tan", size = 5, alpha = .2)
p <- geom_point(data=cardata, aes(x[(i+4)],y[(i+4)]), colour = "tan", size = 6, alpha = .3)
g <- g + m + n + o + p
plot(g)
ani.pause()
i<-i+1 } #end of while loop
},#end of first parameter in saveGIF
interval = 0.03, movie.name = "car.gif",
ani.width = 600, ani.height = 600)
There are many techniques you can use to make an animation easier to follow. As an added benefit it also makes it look more polished. For example you can add a “comet effect”. This is when a moving object leaves a trail behind it. One way to achieve this is by plotting several consecutive points in the data. The trailing points ones have increasing transparency (the lower the alpha the more the transparency). In my chart transparency wasn’t showing well so instead I made each trailing points smaller than the one ahead of it. This worked for my chart because the speed of the lead data point is really slow, if it had jumped a large distance it would leave behind the other points giving me several frames where I have four separate data points instead one with a tail.
Talking about speed, a lot of my animations are very slow because I wanted them to be smooth. Typically the slower the animation, the smoother it will look, just don’t let it get painfully slow. The easiest way to speed up an animation is to decrease the interval time (which decreases the amount of time between frames). The other route to speed up an animation is decreasing the amount of frames you have in your animation. To achieve this you simply decrease the amount of iterations in your loop. If you were using a while loop like I did; instead of increasing i by 1 point in every loop you can increase it by 3. This would triple the speed by decrease the amount of frames in the overall animation.
t = seq(0,1.1*pi,.01)
x = cos(2*t)*(cos(t)^2)
y = sin(2*t)*(sin(t)^2)
df <- data.frame(t,x,y)
i<-345
g <- ggplot(df, aes(x,y)) +
theme(panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
line = element_blank(),
panel.background = element_rect(fill = "transparent",colour = NA),
plot.background = element_rect(fill = "transparent",colour = NA)) +
xlim(c(-1, 1)) + ylim(c(-1, 1)) +
geom_point(data=df[1:i,], aes(x,y), colour = "pink", size = 2)
png(file="testfile.png", width=550, height=550, bg = "transparent")
plot(g)
dev.off()
Keep in mind the default background color for a graph is white (grey for ggplot). White does not mean transparent. To make a transparent background in ggplot, the panel background and plot background must be filled transparent and color = NA.
While it is not absolutely necessary to get rid of the major and minor panel lines, I suggest you do because they look really ugly and usually do not contribute useful information to your graph. You can make the transparent GIF by running the saveGIF function on a series of transparent graphs.
i<-1 while (i < length(x)) {
g <- ggplot(df, aes(x,y)) +
theme(panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
line = element_blank(),
panel.background = element_rect(fill = '#000000',colour = '#000000'),
plot.background = element_rect(fill = '#000000',colour = '#000000')) +
xlim(c(-1, 1)) + ylim(c(-1, 1)) +
geom_point(data=df[1:i,], aes(x,y), colour = "pink", size = 2)
id<- 100 + i
filename <- paste("transptriangle", id, ".png", sep="")
png(file=filename, width=550, height=550, bg = "transparent")
plot(g)
dev.off()
i<-i+1 }
system("convert -delay 40 *.png input2.gif")
system("convert input2.gif -transparent black output2.gif")
saveGIF is even less reliable when making GIFs with transparent backgrounds so I suggest you save all of the images to disk and set the backgrounds to either solid white or solid black. Use hex code to make sure it is pure white (#ffffff) or black (#000000). Then with the system command you can use Imagemagick to convert the PNGs into a GIF. You can then run Imagemagick again to make the background for the GIF transparent.
Conclusion Animation in R is easy although it can be quite frustrating to start. While animated graphs look great they should be used judiciously. Don’t animate graphs just because you can. If the animated graph does not help highlight useful information than it is usually better to leave the graph as a static image. It is important to be aware of the speed and cleanliness of the graph. Try to declare axis values if you can because if you do not, the default axis values may change, causing the data points to jump around. If your graph is too fast it can look clunky but if it is too slow viewers will get bored watching it. There will be plenty of trial and error to working towards the best settings for a particular data set. Lastly, I strongly suggest making animations the HTM format. After having found many issues with the saveGIF function and ImageMagic I do not recommend it’s use. Only use the GIF format when you have to.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.