Save multiple ggplot2 plots as R object in list and re-displaying in grid
Asked Answered
M

2

3

I would like to save multiple plots (with ggplot2) to a list during a large for-loop. And then subsequently display the images in a grid (with grid.arrange)

I have tried two solutions to this: 1 storing it in a list, like so:

pltlist[["qplot"]] <- qplot 

however for some reason this does save the plot correctly. So I resorted to a second strategy which is recordPlot() This was able to save the plot correctly, but unable to use it in a grid.

Reproducable Example:

require(ggplot2);require(grid);require(gridExtra)
df  <- data.frame(x = rnorm(100),y = rnorm(100))
histoplot <- ggplot(df, aes(x=x)) + geom_histogram(aes(y=..density..),binwidth=.1,colour="black", fill="white") 
qplot <- qplot(sample = df$y, stat="qq") 



pltlist <- list()
pltlist[["qplot"]] <- qplot
pltlist[["histoplot"]] <- histoplot
grid.arrange(pltlist[["qplot"]],pltlist[["histoplot"]], ncol=2) 

above code works but produces the wrong graph in my actual code

Then I tried recordPlot()

print(histoplot)
c1 <- recordPlot()
print(qplot)
c2 <- recordPlot()

I am able to display all the plots individually but grid.arrange produces an error:

grid.arrange(replayPlot(c1),replayPlot(c2), ncol=2) # = Error

Error in gList(list(wrapvp = list(x = 0.5, y = 0.5, width = 1, height = 1, : only 'grobs' allowed in "gList"

In this thread Saving grid.arrange() plot to file They dicuss a solution which utilizes arrangeGrob() instead

arrangeGrob(c1, c1, ncol=2) # Error

Error in vapply(x$grobs, as.character, character(1)) : values must be length 1, but FUN(X[[1]]) result is length 3

I am forced to use the recordPlot() instead of saving to a list since this does not produce the same graph when saved as when it is plotted immediately, which I unfortunately cannot replicate, sorry.

In my actual code I am doing a large for-loop, looping through several variables, making a correlation with each and making scatterplots, where I name the scatterplots dependent on their significans level. I then want to re-display the plots that were significant in a grid, in a dynamic knitr report.

I am aware that I could just re-plot the plots that were significant after the for-loop instead of saving them, (I can't save as png while doing knitr either). However I would like to find a way to dynammically save the plots as R-objects and then replot them in a grid afterwards.

Thanks for Reading

"R version 3.2.1" Windows 7 64bit - RStudio - Version 0.99.652 attached base packages: [1] grid grDevices datasets utils graphics stats methods base

other attached packages: [1] gridExtra_2.0.0 ggplot2_1.0.1

Mounts answered 11/8, 2015 at 16:59 Comment(6)
Along these lines? lst <- list(qplot(1,1), qplot(1,1), qplot(1,1)) ;do.call(grid.arrange, c(lst, ncol=2))Cowbind
possible duplicate of Grid of multiple ggplot2 plots which have been made in a for loopCowbind
lst <- list(replayPlot(c1),replayPlot(c2)) ;do.call(grid.arrange, c(lst, ncol=2)) Produces same Error Error in gList(list(wrapvp = list(x = 0.5, y = 0.5, width = 1, height = 1, : only 'grobs' allowed in "gList" The Thread: #9316111 Provides no solution to displaying specific plots, neither does it use the recordPlot() function.Mounts
it looks as though the real problem is something you cannot replicate in this minimal example, so unfortunately there isn't much one can say until you succeed in isolating the problem.Suppose
I agree, Thanks anyways I've resorted to encapsulating a function for creating the ggplots, so i can redraw them again quickly. But if there are people in the future who find out how to save plots to a list, i would like to know.Mounts
I've found a solution, after a bit of trouble for creating functions for ggplot #31994887Mounts
E
1

I can think of two solutions.

1. If your goal is to just save the list of plots as R objects, I recommend:

    saveRDS(object = pltlist, file = "file_path")

This way when you wish to reload in these graphs, you can just use readRDS(). You can then put them in cowplot or gridarrange. This command works for all lists and R Objects.

One caveat to this approach is if settings/labeling for ggplot2 is dependent upon things in the environment (not the data, but stuff like settings for point size, shape, or coloring) instead of the ggplot2 function used to make the graph), your graphs won't work until you restore your dependencies. One reason to save some dependencies is to modularize your scripts to make the graphs.

Another caveat is performance: From my experience, I found it is actually faster to read in the data and remake individual graphs than load in an RDS file of all the graphs when you have a large number of graphs (100+ graphs).

2. If your goal is to save an 'image' or 'picture' of each graph (single and/or multiplot as .png, .jpeg, etc.), and later adjust things in a grid manually outside of R such as powerpoint or photoshop, I recommend:

    filenames <- c("Filename_1", "Filename_2") #actual file names you want... 
    lapply(seq_along(pltlist), function(i) {
      ggsave(filename = filenames[i], plot = pltlist[[i]], ...) #use your settings here
    })

Settings I like for single plots:

    lapply(seq_along(pltlist), function(i) ggsave(
      plot = pltlist[[i]], 
      filename = paste0("plot_", i, "_", ".tiff"), #you can even paste in pltlist[[i]]$labels$title
      device = "tiff", width=180, height=180, units="mm", dpi=300, compression = "lzw", #compression for tiff
      path = paste0("../Blabla") #must be an existing directory.
    ))

You may want to do the manual approach if you're really OCD about the grid arrangement and you don't have too many of them to make for publications. Otherwise, when you do grid.arrange you'll want to do all the specifications there (adjusting font, increasing axis label size, custom colors, etc.), then adjust the width and height accordingly.

Enthalpy answered 24/4, 2018 at 18:45 Comment(0)
B
0

Reviving this post to add multiplot here, as it fits exactly.

require(ggplot2)

mydd <- setNames( data.frame( matrix( rep(c("x","y","z"), each=10) ),
 c(rnorm(10), rnorm(10), rnorm(10)) ), c("points", "data") )
#    points         data
# 1       x  0.733013658
# 2       x  0.218838717
# 3       x -0.008303382
# 4       x  2.225820069
# ...

p1 <- ggplot( mydd[mydd$point == "x",] ) + geom_line( aes( 1:10, data, col=points ) )
p2 <- ggplot( mydd[mydd$point == "y",] ) + geom_line( aes( 1:10, data, col=points ) )
p3 <- ggplot( mydd[mydd$point == "z",] ) + geom_line( aes( 1:10, data, col=points ) )

multiplot(p1,p2,p3, cols=1)

multiplot:

multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) {
  library(grid)

  # Make a list from the ... arguments and plotlist
  plots <- c(list(...), plotlist)

  numPlots = length(plots)

  # If layout is NULL, then use 'cols' to determine layout
  if (is.null(layout)) {
    # Make the panel
    # ncol: Number of columns of plots
    # nrow: Number of rows needed, calculated from # of cols
    layout <- matrix(seq(1, cols * ceiling(numPlots/cols)),
                    ncol = cols, nrow = ceiling(numPlots/cols))
  }

 if (numPlots==1) {
    print(plots[[1]])

  } else {
    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout))))

    # Make each plot, in the correct location
    for (i in 1:numPlots) {
      # Get the i,j matrix positions of the regions that contain this subplot
      matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE))

      print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
                                      layout.pos.col = matchidx$col))
    }
  }
}

Result:

enter image description here

Brainy answered 16/12, 2020 at 3:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.