Saving plots in R as GIFs

TMS picture TMS · Aug 19, 2013 · Viewed 12.7k times · Source

In R, I use function savePlot to save graphs into image files. But my colleague can only open .jpgs and .gifs (probably because he's on vacation, reading emails on his mobile phone). I hate to create jpegs because especially the boxplots looks very ugly (whiskers blurred etc.). But the savePlot function only supports the following types:

type = c("wmf", "emf", "png", "jpg", "jpeg", "bmp",
                  "tif", "tiff", "ps", "eps", "pdf")

How can I save plot in GIF in R?

EDIT: if possible, ideal solution should work without installing ImageMagick (so that the R script is easily portable).

Answer

Ben Bolker picture Ben Bolker · Aug 19, 2013

R doesn't have a native GIF graphics driver, mostly (completely?) due to the patent-encumbrances of the GIF format: see http://tolstoy.newcastle.edu.au/R/help/05/02/12809.html .

There is a function in the caTools package (write.gif()) but it is specifically designed for writing images. If you wanted to use it you have to do something hacky to convert your plot to an image first (e.g. save as PNG and then read it back into R as an image). For example:

png("myPlot.png")
plot(rnorm(1000),rnorm(1000))
dev.off()
library(png)
P1 <- readPNG("myPlot.png")
library(caTools)
write.gif(P1,"myPlot.gif")
showGIF <- function(fn) system(paste("display",fn))
showGIF("myPlot.gif")
unlink("myPlot.gif")  ## clean up

?write.gif() has a lot of stuff about color indexing that I didn't read but that might be important for more complex graphs ...

The animation package has a saveGIF() function to save GIFs, but (1) it is designed for saving multi-frame animations (not general graphics), and (2) it does it by calling ImageMagick.

It's easier just to construct that function yourself.

For example:

png("myPlot.png")
plot(rnorm(1000),rnorm(1000))
dev.off()
system("convert myPlot.png myPlot.gif")
unlink("myPlot.png") ## clean up
showGIF("myPlot.gif")
unlink("myPlot.gif") ## clean up

Of course you can either of these in a function if you want to use them regularly.

UPDATE: I spent a while longer on this, to try to get a pure-R solution, but don't yet have a working solution. Suggestions or edits welcome ...

## needs ImageMagick: just for testing ...
showGIF <- function(fn) system(paste("display",fn))

The main function:

saveGIF <- function(fn,verbose=FALSE,debug=FALSE) {
    require(png)
    require(caTools)
    tmpfn <- tempfile()
    on.exit(unlink(tmpfn))
    savePlot(tmpfn,type="png")
    P1 <- readPNG(tmpfn)
    dd <- dim(P1)
    P1 <- aperm(P1,c(3,1,2),resize=TRUE)  ## P1[,1,15]
    dim(P1) <- c(dd[3],prod(dd[1:2]))
    P1 <- t(P1)
    if (verbose) cat("finding unique colours ...\n")
    P1u <- unique(P1)
    rgbMat <- function(x) {
        rgb(x[,1],x[,2],x[,3])
    }
    if (verbose) cat("creating colour index ...\n")
    pp <- paste(P1[,1],P1[,2],P1[,3],sep=".")
    ## make sure factor is correctly ordered
    ind <- as.numeric(factor(pp,levels=unique(pp))) 
    if (verbose) cat("finding colour palette ...\n")
    if (nrow(P1u)>256) {
        if (verbose) cat("kmeans clustering ...\n")
        kk <- kmeans(P1u,centers=256)
        ind <- kk$cluster[ind]
        pal <- rgbMat(kk$centers)
    } else {
        pal <- rgbMat(P1u)
    }
    ## test:
    if (debug) {
        dev.new()
        par(mar=rep(0,4))
        image(t(matrix(ind-1,nrow=dd[1])),col=pal,axes=FALSE,ann=FALSE)
    }
    if (verbose) cat("writing GIF ...\n")
    indmat <- matrix(ind-1,nrow=dd[1])
    storage.mode(indmat) <- "integer"
    write.gif(indmat,fn,col=as.list(pal),scale="never")
}


X11.options(antialias="none")
image(matrix(1:64,nrow=8),col=rainbow(10))
saveGIF("tmp.gif",verbose=TRUE,debug=TRUE)
showGIF("tmp.gif")