Saturday, November 5, 2016

Basic Animations with R Shiny, the Animation Library, and ImageMagick


In this post I take a look at the use of animation in R with the Shiny and Animation libraries. The animations are created from a series of individual graphs plotted by R and then stitched together to form an animation.

The below code sample was obtained from the Analysis Programming blog ( http://alstatr.blogspot.com/2014/02/r-animating-2d-and-3d-plots.html ):


library(animation)

saveGIF({
  for(i in 1:150){
    x <- seq(-6 + (i * 0.05), 6 + (i * 0.05), length= 100)
    y <- x
    f <- function(x, y) { sin(x) + cos(y) }
    z <- outer(x, y, f)
    persp(x, y, z, theta = 45 + (i * 0.5), phi = 35, expand = 0.4, col = "lightblue")
  }
}, interval = 0.1, ani.width = 550, ani.height = 550)


The saveGIF() function has some non-R dependencies in order to convert a series of PNG format still images created by the animation routine into an animated GIF. The dependency was resolved by installing ImageMagick ( http://imagemagick.org/script/binary-releases.php ) and then restarting R Studio. 


The resulting animated GIF file of the sin(x)+cos(y) function is below. The persp() function call rotates the view of the graph.

Having found a successful piece of sample code and generated an animated GIF from a series of PNG graph plots I decided to modify the working code and replace the function with something different and see if I could have that code work successfully.

In the interests of simplicity and not requiring external data sets I decided to animate a series of histogram plots of generated random numbers and plot a normal line over the histogram. Using unweighted random numbers does not make for an interesting histogram, however, so I also had to create a random set of weights to be used when generating the random numbers. 

The sample() function is well suited for these tasks with the mix vector being assigned 25 random numbers between 1 and 100 with replacement. This mix variable is then used to create the dist vector that is simply the original value divided by 100 to obtain a probability value we will use in the next sample() function call.

I generated only 25 random numbers for our probabilities because the random numbers we will be plotting in the histogram will have the potential values between 1 and 25 so we need exactly 25 probabilities to pass to sample() as the probability distribution vector when we generate 100 values between 1 and 25.

saveGIF({
  for(i in 1:100){
    mix <- sample(1:100, 25, replace = T)
    dist <- t(t(mix) / 100)
    set <- sample(1:25, 100, replace = T, prob = dist)

    h<-hist(set, breaks=10, density=10, col="darkblue") 
    xfit<-seq(min(set),max(set)) 
    yfit<-dnorm(xfit,mean=mean(set),sd=sd(set)) 
    yfit <- yfit*diff(h$mids[1:2])*length(set) 
    lines(xfit, yfit, col="red", lwd=2)
    }
  }, interval = 0.3, ani.width = 600, ani.height=600)


This set of 100 random values is then plotted with a histogram and a normal line fitted to the plot. All of this code is encapsulated in a for-loop that iterates 100 times creating 100 PNG images which are then passed to the saveGIF() function that calls upon ImageMagick to create the animated GIF. I slowed down the interval for saveGIF slightly to 0.3 as the prior value of 0.1 flipped through the images a little too quickly for a set of histogram plots compared to a surface plot of sin and cos.




No comments:

Post a Comment