4

Im trying to use the caTools package to combine multiple plots into a gif.

My basic code looks like :

 for( i in 1:100){
     plot(....) // plots few points and lines, changes slightly with each i
  }

I would like to combine these to a gif to see the "evolution" of the plot.

However for write.gif() from caTools, I need to give an image as an input. For each i, how do I convert the plot into an image without

  1. saving to disc as an intermediate step
  2. depending on ImageMagick or similar external dependencies.

Please feel free to point out if this is a duplicate. [ Creating a Movie from a Series of Plots in R doesnt seem to answer this ]

EDIT: Basically this requires us to convert a plot to a matrix. Since this very likely happens every time someone saves a plot, it should not be very difficult. However Im not able to get hold of how to exactly do this.

Community
  • 1
  • 1
Sujay
  • 87
  • 1
  • 8
  • why the restriction on using external software? – hrbrmstr Sep 07 '15 at 16:33
  • Maybe the ``gridBase`` package or the ``?par`` function could help. – holzben Sep 07 '15 at 19:35
  • @hrbrmstr mainly for portability reason – Sujay Sep 07 '15 at 20:21
  • @holzben :: gridBase seems to play with par() options. Any hint on how this can be used ? – Sujay Sep 07 '15 at 20:45
  • 1
    @Sujay I can't think of a single OS that ImageMagick doesn't work on and/or has a binary build for. – hrbrmstr Sep 07 '15 at 21:18
  • @Sujay that`s true but i experienced that ``par`` can be quiet tricky to use. ``gridBase`` and also ``gridExtra`` are easier to use. This post gives a quiet good example: http://stackoverflow.com/questions/9490482/combined-plot-of-ggplot2-not-in-a-single-plot-using-par-or-layout-functio – holzben Sep 08 '15 at 19:38

3 Answers3

8

I suggest using the animation package and ImageMagick instead:

library(animation)
## make sure ImageMagick has been installed in your system
saveGIF({
  for (i in 1:10) plot(runif(10), ylim = 0:1)
})

Otherwise you could try it in the veins of this (plenty of room for optimization):

library(png)
library(caTools)
library(abind)

# create gif frames and write them to pngs in a temp dir 
dir.create(dir <- tempfile(""))
for (i in 1:8) {
  png(file.path(dir, paste0(sprintf("%04d", i), ".png")))
  plot(runif(10), ylim = 0:1, col = i)
  dev.off()
}

# read pngs, create global palette, convert rasters to integer arrays and write animated gif
imgs <- lapply(list.files(dir, full.names = T), function(fn) as.raster(readPNG(fn)))
frames <- abind(imgs, along = 3) # combine raster pngs in list to an array 
cols <- unique(as.vector(frames)) # determine unique colors, should be less then 257
frames <- aperm(array(match(frames, cols) - 1, dim = dim(frames)), c(2,1,3)) # replace rgb color codes (#ffffff) by integer indices in cols, beginning with 0 (note: array has to be transposed again, otherwise images are flipped) 
write.gif(
  image = frames, # array of integers 
  filename = tf <- tempfile(fileext = ".gif"), # create temporary filename
  delay = 100, # 100/100=1 second delay between frames 
  col = c(cols, rep("#FFFFFF", 256-length(cols))) # color palette with 256 colors (fill unused color indices with white) 
)

# open gif (windows)
shell.exec(tf)  
lukeA
  • 53,097
  • 5
  • 97
  • 100
  • 1
    I am looking for a solution without ImageMagick. – Sujay Sep 07 '15 at 16:06
  • Check out the other possibility using b/w temporary images. – lukeA Sep 08 '15 at 07:17
  • How can I keep the original color if I import .png? @lukeA – Iris Sep 08 '15 at 10:00
  • Thank you @lukeA , but yes, there is a lot of room for optimization ;) Btw, I got troubles with `256-length(cols)` because in my case `length(cols)==291`. – Iris Sep 08 '15 at 11:26
  • Hmm how did you create your images? GIF supports 256 colors. Seems as if `save.gif` only supports a global palette, not one palette for every frame. Maybe k-means-cluster them down to 256? All in all it's questionable if all this is worth the effort - ImageMagick is obviously an easier choice. – lukeA Sep 08 '15 at 11:58
  • I have a list of png created in R `png()` and they should have only a 255 colors. I have no idea where the additonal colors come from. But I think instead of spending time on this problem, now, I will simple use Imagemagick directly. – Iris Sep 08 '15 at 12:14
1

Is that what you are looking for?

   library(ggplot2)
   a <- 0:10
    df <- data.frame(a=a,b=a)
    steps <-function(end){
      a <- ggplot(df[1:end,], aes(a,b)) + 
        geom_point() + 
        scale_x_continuous(limits=c(0,10)) +
        scale_y_continuous(limits=c(0,10))
      print(a)
    }
    gif <- function() {
      lapply(seq(1,11,1), function(i) {
        steps(i)
      })
    }
    library(animation)
    saveGIF(gif(), interval = .2, movie.name="test.gif")
ttlngr
  • 43
  • 7
  • I get an error `Error in in_dir(owd, expr) : could not find function "trace.animate"`. Where is your function `trace.animate()` defined? @ttlngr – Iris Sep 08 '15 at 10:20
  • sorry @Iris , copied that from a script... use function `gif()` (as defined) instead of `trace.animate()` in `saveGIF()`. Edited my answer already. – ttlngr Sep 08 '15 at 10:42
  • Unfortunately, I run into this error with your answer http://stackoverflow.com/questions/28142300/r-gif-animation-with-imagemagick – Iris Sep 08 '15 at 11:28
  • Did you make sure ImageMagick has been installed? Have you tried getting the animation package from github? – ttlngr Sep 08 '15 at 11:38
  • ImageMagick is installed and I can work with it. The solution in the link I posted doesn't work for me as `package ‘devtools’ is not available (for R version 3.0.1)` – Iris Sep 08 '15 at 11:59
  • Well, then i see no other solution than upgrading to newer version of R. Mine is 3.0.2. – ttlngr Sep 08 '15 at 12:26
0

I liked @ttlngr's answer but I wanted something a bit simpler (it still uses ImageMagick).

saveGIF({
  for (i in 1:10){  
    a <- ggplot(df[1:i,], aes(a,b)) + 
      geom_point() + 
      scale_x_continuous(limits=c(0,10)) +
      scale_y_continuous(limits=c(0,10))
  print(a)}
}, interval = .2, movie.name="test.gif")
Nova
  • 5,423
  • 2
  • 42
  • 62