How to save multiple ggplot charts in loop for using grid.arrange - r

I have a for loop that creates a different ggplot for a different set of parameters each time through the loop. Right now I am printing N different charts one at a time. I would like to save them so I can use grid.arrange to put them all on one page. This doesn't work:
p <- vector(length = N)
for(i in 1:N)
p[i] <- ggplot( ........
...
...
grid.arrange(p[1], p[2], .. p[N], nrow = 4)
Is there a way to save the plots for later plotting a grid of plots on a page outside the loop, or is there a way to set up the grid specification before the loop and and produce the gridded plot on the fly as the loop is executed (e.g., the way par is used with plot)?

You rarely want to use for loops in R. In R's lapply(). In a single step:
do.call(
grid.arrange,
lapply(data, function(f){
ggplot(f, ...)
}
)
EDIT:
If you want to store the list for later plotting:
plot_objects <- lapply(data, function(f) {
ggplot(f, ...)
})
do.call(grid.arrange, plot_objects)

This could be solved by initiating a list to store the plot objects instead of vector
p <- vector('list', N)
for(i in seq_len(N)) {
p[[i]] <- ggplot(...)
}
grid.arrange(p[[1]], p[[2]], ..., p[[N]], nrow = 4)

Related

Saving S3 aggr object in a way mice_plots can be visualized by ggplot in a grid arrangement

I am using a for loop to loop through a dataset and produce a mice_plot per category. In the for loop, I would like to save the produced mice_plot so that I can later plot them all in a grid.
my code looks like this atm:
for center in unique(df$center){
dispatch_centers <- list()
mice_plot <- aggr(center, ...)
# save mice_plot some how in a way I can later open it with ggplot
as.grob()?
#add to list
center_plots[[center]] <- mice_plot
}
grid.arrange(grobs = center_plots, ncol = 2)
However, I do not understand the aggr() object nor how to save it so I can later handle it with ggplot2. Does anyone have any ideas?

Prevent a plot to be overwrite in a for loop

I am trying to create three different plots in a for loop and then plotting them together in the same graph.
I know that some questions regarding this topic have already been asked. But I do not know what I am doing wrong. Why is my plot being overwritten.
Nevertheless, I tried both solutions (creating a list or using assign function) and I do not know why I get my plot overwriten at the end of the loop.
So, the first solution is to create a list:
library(gridExtra)
library(ggplot2)
out<-list()
for (i in c(1,2,4)){
print(i)
name= paste("WT.1",colnames(WT.1#meta.data[i]), sep=" ")
print(name)
out[[length(out) + 1]] <- qplot(NEW.1#meta.data[i],
geom="density",
main= name)
print(out[[i]])
}
grid.arrange(out[[1]], out[[2]], out[[3]], nrow = 2)
When I print the plot inside the loop, I get what I want...but of course they are not together.
First Plot
When I plot them all together at the end, I get the same plot for all of the three: the last Plot I did.
All together
This is the second option: assign function. I have exactly the same problem.
for ( i in c(1,2,4)) {
assign(paste("WT.1",colnames(WT.1#meta.data[i]),sep="."),
qplot(NEW.1#meta.data[i],geom="density",
main=paste0("WT.1",colnames(WT.1#meta.data[i]))))
}
You're missing to dev.off inside the loop for every iteration. Reproducible code below:
library(gridExtra)
library(ggplot2)
out<-list()
for (i in c(1,2,3)){
print(i)
out[[i]] <- qplot(1:100, rnorm(100), colour = runif(100))
print(out[[i]])
dev.off()
}
grid.arrange(out[[1]], out[[2]], out[[3]], nrow = 2)

How do I save or plot from within lists

I have named lists of entities (objects, lists, grobs?), from qplot or ggplot, that render or save just fine on their own, but I can't figure out how to pass them as a list or vector for arrangement. I believe my issue is with extracting list objects generally rather than with ggplot2.
library(ggplot2)
library(grid)
library(gridExtra)
# Generate a named list of ggplots
plotlist <- list()
for(a in c(1:4)) {
for(b in c(1:4)) {
plotlist[[paste0("Z",a,b)]] <-
qplot(rnorm(40,a,b),
geom="histogram",
xlab=paste0("Z",a,b))
}
}
# Arrange and display them
# The following two lines work fine, so the plots are in there:
plotlist[["Z12"]]
ggsave(plot=plotlist[["Z12"]], filename="deletable.png")
# The following two lines complain about not being 'grobs'
grid.arrange(plotlist, widths=c(1,1), ncol=2)
grid.arrange(unlist(plotlist), widths=c(1,1), ncol=2)
Can I somehow cast them as grobs without naming them explicitly, or find an alternative to unlist that lets the grob out?
Use lapply(plotlist, ggplot2::ggplotGrob) to generate a list of ggplot2 grobs. This list of grobs can then be passed to gridExtra::grid.arrange.
For example:
library(ggplot2)
library(gridExtra)
plotlist <- list()
for(a in c(1:4)) {
for(b in c(1:4)) {
plotlist[[paste0("Z",a,b)]] <-
qplot(rnorm(40,a,b),
geom="histogram",
xlab=paste0("Z",a,b))
}
}
grid.arrange(grobs = lapply(plotlist, ggplotGrob), widths = c(1, 1), ncol = 2)

Plotting subsets of an AffyRNAdeg {affy} object with plot AffyRNAdeg?

library(affy)
microarrays <- ReadAffy() # 98 CEL files are read into the same object
RNAdeg <- AffyRNAdeg(microarrays)
Now I want to plot subsets of RNAdeg
plotAffyRNAdeg(RNAdeg[.......?]) # What can I do?
I've tried various 'for' loops without success.
But if plot line colors are specified then plotAffyRNAdeg plots a subset of 1:(number of colors specified), but I haven't thought of a way to use that effectively. For example, below plots the first through the sixth AffyRNAdeg'd set of microarray data (first through sixth .CEL file read in by ReadAffy() )
plotAffyRNAdeg(RNAdeg,col=c(2,2,2,3,3,3))
OK, one way was found by running AffyRNAdeg() on subsets of the object the CEL files are in and putting the resulting data in a list of lists organized by experiment, then plotting the list elements. Maybe there is an easier way, but this worked (I'm quite new to R).
library(affy)
library(RColorBrewer)
> sampleNames(ARTHwoundMA[,11:14])
[1] "GSE18960_05_GSM469416_trt_rep2.CEL" "GSE18960_06_GSM469418_trt_rep3.CEL"
[3] "GSE5525_GSM128715_ctrl12h.CEL" "GSE5525_GSM128716_ctrl24h.CEL
# RNA DEG
# Indices to subset by experiment
cel_names <- substr(sampleNames(ARTHwoundMA),1,7)
unique_exp <- unique(substr(sampleNames(ARTHwoundMA),1,7))
exp_ind <- list()
for (i in 1:length(unique_exp))
{
tempvec <- vector()
for (j in 1:length(cel_names))
{
if (cel_names[j]==unique_exp[i])
{
tempvec <- append(tempvec,j)
}
}
exp_ind[[(length(exp_ind)+1)]] <- tempvec
}
# Calculating
RNAdeg_exp <- list()
for(i in 1:length(exp_ind))
{
RNAdeg_exp[[i]] <- AffyRNAdeg(ARTHwoundMA[,exp_ind[[i]]])
}
# Plotting
colors <- colorRampPalette(rev(brewer.pal(9, "Reds")))(length(exp_ind[[i]])
pdf(file="C:\\R working directory\\TEST\\RNAdeg_plots.pdf")
for(i in 1:length(exp_ind))
{
par(bg="gray")
colors <- colorRampPalette(rev(brewer.pal(9, "Reds")))(length(exp_ind[[i]]))
plotAffyRNAdeg(RNAdeg_exp[[i]], col=colors)
plot.new()
legend("topleft", lty=1, lwd=2,col=colors,
legend=paste(sampleNames(ARTHwoundMA[,exp_ind[[i]]])))
}
dev.off()

How can I arrange an arbitrary number of ggplots using grid.arrange?

This is cross-posted on the ggplot2 google group
My situation is that I'm working on a function that outputs an arbitrary number of plots (depending upon the input data supplied by the user). The function returns a list of n plots, and I'd like to lay those plots out in 2 x 2 formation. I'm struggling with the simultaneous problems of:
How can I allow the flexibility to be handed an arbitrary (n) number of plots?
How can I also specify I want them laid out 2 x 2
My current strategy uses grid.arrange from the gridExtra package. It's probably not optimal, especially since, and this is key, it totally doesn't work. Here's my commented sample code, experimenting with three plots:
library(ggplot2)
library(gridExtra)
x <- qplot(mpg, disp, data = mtcars)
y <- qplot(hp, wt, data = mtcars)
z <- qplot(qsec, wt, data = mtcars)
# A normal, plain-jane call to grid.arrange is fine for displaying all my plots
grid.arrange(x, y, z)
# But, for my purposes, I need a 2 x 2 layout. So the command below works acceptably.
grid.arrange(x, y, z, nrow = 2, ncol = 2)
# The problem is that the function I'm developing outputs a LIST of an arbitrary
# number plots, and I'd like to be able to plot every plot in the list on a 2 x 2
# laid-out page. I can at least plot a list of plots by constructing a do.call()
# expression, below. (Note: it totally even surprises me that this do.call expression
# DOES work. I'm astounded.)
plot.list <- list(x, y, z)
do.call(grid.arrange, plot.list)
# But now I need 2 x 2 pages. No problem, right? Since do.call() is taking a list of
# arguments, I'll just add my grid.layout arguments to the list. Since grid.arrange is
# supposed to pass layout arguments along to grid.layout anyway, this should work.
args.list <- c(plot.list, "nrow = 2", "ncol = 2")
# Except that the line below is going to fail, producing an "input must be grobs!"
# error
do.call(grid.arrange, args.list)
As I am wont to do, I humbly huddle in the corner, eagerly awaiting the sagacious feedback of a community far wiser than I. Especially if I'm making this harder than it needs to be.
You're ALMOST there! The problem is that do.call expects your args to be in a named list object. You've put them in the list, but as character strings, not named list items.
I think this should work:
args.list <- c(plot.list, 2,2)
names(args.list) <- c("x", "y", "z", "nrow", "ncol")
as Ben and Joshua pointed out in the comments, I could have assigned names when I created the list:
args.list <- c(plot.list,list(nrow=2,ncol=2))
or
args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2)
Try this,
require(ggplot2)
require(gridExtra)
plots <- lapply(1:11, function(.x) qplot(1:10,rnorm(10), main=paste("plot",.x)))
params <- list(nrow=2, ncol=2)
n <- with(params, nrow*ncol)
## add one page if division is not complete
pages <- length(plots) %/% n + as.logical(length(plots) %% n)
groups <- split(seq_along(plots),
gl(pages, n, length(plots)))
pl <-
lapply(names(groups), function(g)
{
do.call(arrangeGrob, c(plots[groups[[g]]], params,
list(main=paste("page", g, "of", pages))))
})
class(pl) <- c("arrangelist", "ggplot", class(pl))
print.arrangelist = function(x, ...) lapply(x, function(.x) {
if(dev.interactive()) dev.new() else grid.newpage()
grid.draw(.x)
}, ...)
## interactive use; open new devices
pl
## non-interactive use, multipage pdf
ggsave("multipage.pdf", pl)
I'm answering a bit late, but stumbled on a solution at the R Graphics Cookbook that does something very similar using a custom function called multiplot. Perhaps it will help others who find this question. I'm also adding the answer as the solution may be newer than the other answers to this question.
Multiple graphs on one page (ggplot2)
Here's the current function, though please use the above link, as the author noted that it's been updated for ggplot2 0.9.3, which indicates it may change again.
# Multiple plot function
#
# ggplot objects can be passed in ..., or to plotlist (as a list of ggplot objects)
# - cols: Number of columns in layout
# - layout: A matrix specifying the layout. If present, 'cols' is ignored.
#
# If the layout is something like matrix(c(1,2,3,3), nrow=2, byrow=TRUE),
# then plot 1 will go in the upper left, 2 will go in the upper right, and
# 3 will go all the way across the bottom.
#
multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) {
require(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))
}
}
}
One creates plot objects:
p1 <- ggplot(...)
p2 <- ggplot(...)
# etc.
And then passes them to multiplot:
multiplot(p1, p2, ..., cols = n)

Resources