Plot ggplots stored in list over several pages - r

I know similar questions have been already asked so sorry if this is a redundant question! However, I can't seem to find a solution that arranges several ggplots from a list onto 1 page over several pages.
I have a list of approximately 100 ggplots - I want to plot every 4 ggplots on 1 page, and iterate through the list until all the ggplots have been plotted. I then want to export the approximately 25 pages to a single pdf file.
So far, I've tried:
pdf("plots.pdf", onefile = TRUE, width = 11, height = 8.5)
for (i in 0:24) {
ggarrange(list[[4i+1]], list[[4i+2]], list[[4i+3]], list[[4i+4]],
nrow = 2, ncol = 2, common.legend = TRUE, legend = 'bottom'
}
dev.off()
However, I'm getting the error that the subscript is out of bounds. I've tried narrowing the range in the for loop to try to overcome this error but it's returning the same error. I also know we can use marrangeGrob(), but I can't seem to add a common legend to the file.
Really appreciate any help!

It would be helpful if you could provide some small list of plots to test on.
I have tried to recreate your scenario, and have found that it wasn't working unless I explicitly print the ggarrange object.
plot <- ggplot(mtcars, aes(x = cyl, y = mpg)) +
geom_point()
plot_list <- list(plot, plot, plot, plot, plot, plot, plot, plot)
pdf("test.pdf", 11, 8.5)
for(i in 0:3){
print(ggarrange(plot_list[[2*i + 1]], plot_list[[2*i + 1]], nrow = 2, ncol = 1))
}
dev.off()
This worked for me. Noting akrun's comment that you forgot your * symbol.

Related

Print several plots on one PDF page in R

I am trying to plot 4 graphs on one single PDF page. Untill now, the only output I got was 1 file per page. I would like to have all of them on one page. One plot is stocked in "c", the three others are produced using a loop. Here is the code I use :
pdf("path_to_file.pdf", onefile = TRUE)
plot(c)
for (exp in expL) {
plot(plot_list[[exp]])
}
dev.off
I tried without the "onefile" specification, or adding :
par(mfrow=c(2,2))
or even
layout(matrix(c(1,2,3,4), ncol = 2, byrow = TRUE))
but nothing helps...
I would be grateful for some help.
Thank you very much!
c should be same type of plot as expL.
If they are, do:
library(cowplot)
plot_grid( append( list(c), expL ), nrow=2 )

Saving ggplot graphs to pdf not formatting correctly in PDF

This is a follow on question to my original here
I am currently trying to save the outputs of a ggplot graph to .pdf but I am running into some problems.
I run the following on the dput data on the original question (I repaste everything below).
library(gridExtra)
pdf("Plots.pdf", onefile = TRUE)
for(j in 1:length(plotList)) {
grid.arrange(plotList[[j]], nrow = 2)
}
dev.off()
This saves the files as a pdf document but instead of getting two graphs per page I get one graph which takes up half a page. Is it possible to resize the graphs, when I select nrow = 3 I get the same problem, I get one graph in the top 3rd / half of the page and a blank space for the rest. I provide a screen shot of the output:
Here is a minimal example:
# Make empty list for plots
plotList <- list()
# Build plots
library(ggplot2)
for(i in 1:2){
plotList[[i]] <-
ggplot(mtcars, aes(mpg, cyl)) +
geom_point() +
labs(title = i)
}
# Save plots
library(gridExtra)
pdf("Plots.pdf", onefile = TRUE)
for(j in 1:length(plotList)) {
grid.arrange(plotList[[j]], nrow = 2)
}
dev.off()
Credit to #LachlanO
You problem comes from how you are calling grid.arrange. By including it in a loop, you are telling it to create multiple separate plots, like this:
grid.arrange(plotList[[1]], nrow = 2)
grid.arrange(plotList[[2]], nrow = 2)
grid.arrange(plotList[[3]], nrow = 2)
What you actually are trying to do is create one grid.arrange object which contains all the plots. To do this, you need call the function against the list:
do.call("grid.arrange", c(plotList, nrow=2))
Alternatively, you can use the cowplot package and use:
cowplot::plot_grid(plotlist = plotList, nrow = 2)
So to save the PDF you can use:
pdf("Plots.pdf", onefile = TRUE)
do.call("grid.arrange", c(plotList, nrow=2))
dev.off()

Printing out a dataframe in R: grid.table outputs cropped tables, doesn't respond to fontsize

I am trying to automate a series of analyses which are intended to save a number of plots for later inspection. One of the plots will be accompanied by a table of values. I'd like to have them in the same pdf so that the users don't have to jump between files.
I have checked numerous questions on SO regarding outputting data frames to pdf, here are a couple of reasons why existing answers aren't satisfactory in my case:
Not familiar with knitr/Sweave
Batch generation of figures mean that I cannot do it manually via RStudio Viewer
grid.table based solutions do not generate the entire table.
Which brings me to my problems, say I have a table 48 x 5 in proportions. If I try to plot it out with grid.table(geno) it results in a cropped table showing some 20-30 rows in the middle. If I go with grid.table(geno, gp = gpar(fontsize=8)) to decrease the fontsize I get the following error message.
Error in gtable_table(d, name = "core", fg_fun = theme$core$fg_fun, bg_fun = theme$core$bg_fun, :
unused argument (gp = list(fontsize = 8)
)
Essentially I would like to be able to use it in this way:
library(grid)
library(gridExtra)
pdf(file="gtype.pdf", title = "Genotype data")
plotGenotype(geno, text_size = 10) # outputs a custom plot
grid.newpage()
grid.table(geno) # grid.table(geno, gp = gpar(fontsize=8))
dev.off()
The problem here is that I either get a cropped table or nothing at all, on the second page. I noticed that many people add height=11, width=8.5 to the pdf() call. I am not sure if/why that would make a difference but setting paper="a4" or height/width according to A4 does not make any difference in my case.
Q1: Is it not possible to get grid.table to resize based on content and not paper?
Q2: Is there some other way to get a data frame printed to a pdf without having to go through LaTeX based solutions?
(I am currently running R 3.3.1 and gridExtra 2.2.1)
Q1: Is it not possible to get grid.table to resize based on content and not paper?
It is possible, but generally not desirable. A table is meant to be read, and if text and spacings were determined by the page rather than the content, it would often yield unreadable results. Thus the usual advice: manually tweak the font size and padding, or split the table.
It is by no means a technical limitation: feel free to set the cell size to fit the page:
grid.newpage()
pushViewport(viewport(width=unit(0.8,"npc"), height=unit(0.8,"npc")))
g <- g2 <- tableGrob(iris[1:4, 1:3], cols = NULL, rows=NULL)
g2$heights <- unit(rep(1/nrow(g2), nrow(g2)), "npc")
grid.arrange(rectGrob(), rectGrob(), nrow=1, newpage = FALSE)
grid.arrange(g, g2, nrow=1, newpage = FALSE)
but with too much content for the page it's unclear what result is better
grid.newpage()
pushViewport(viewport(width=unit(0.8,"npc"), height=unit(0.8,"npc")))
g <- g2 <- tableGrob(iris[1:20, 1:3], cols = NULL, rows=NULL)
g3 <- tableGrob(iris[1:20, 1:3], cols = NULL, rows=NULL, theme=ttheme_default(base_size=7))
g2$heights <- g3$heights <- unit(rep(1/nrow(g2), nrow(g2)), "npc")
grid.arrange(rectGrob(), rectGrob(), rectGrob(), nrow=1, newpage = FALSE)
grid.arrange(g, g2, g3, nrow=1, newpage = FALSE)
If the page size can be changed, it is usually the best option. One can query the table size before drawing, convert it to inches, and pass it to the device.
g1 <- tableGrob(iris[1:4, 1:5])
g2 <- tableGrob(iris[1:20, 1:5])
maxheight <- convertHeight(sum(g2$heights), "in", TRUE)
pdf("fit.pdf", height=maxheight)
grid.draw(g1)
grid.newpage()
grid.draw(g2)
dev.off()
However, as far as I know all pages in a given pdf will have to have the same size (there might be ways around it, but tricky).

ggplot2 : printing multiple plots in one page with a loop

I have several subjects for which I need to generate a plot, as I have many subjects I'd like to have several plots in one page rather than one figure for subject.
Here it is what I have done so far:
Read txt file with subjects name
subjs <- scan ("ListSubjs.txt", what = "")
Create a list to hold plot objects
pltList <- list()
for(s in 1:length(subjs))
{
setwd(file.path("C:/Users/", subjs[[s]])) #load subj directory
ifile=paste("Co","data.txt",sep="",collapse=NULL) #Read subj file
dat = read.table(ifile)
dat <- unlist(dat, use.names = FALSE) #make dat usable for ggplot2
df <- data.frame(dat)
pltList[[s]]<- print(ggplot( df, aes(x=dat)) + #save each plot with unique name
geom_histogram(binwidth=.01, colour="cyan", fill="cyan") +
geom_vline(aes(xintercept=0), # Ignore NA values for mean
color="red", linetype="dashed", size=1)+
xlab(paste("Co_data", subjs[[s]] , sep=" ",collapse=NULL)))
}
At this point I can display the single plots for example by
print (pltList[1]) #will print first plot
print(pltList[2]) # will print second plot
I d like to have a solution by which several plots are displayed in the same page, I 've tried something along the lines of previous posts but I don't manage to make it work
for example:
for (p in seq(length(pltList))) {
do.call("grid.arrange", pltList[[p]])
}
gives me the following error
Error in arrangeGrob(..., as.table = as.table, clip = clip, main = main, :
input must be grobs!
I can use more basic graphing features, but I d like to achieve this by using ggplot. Many thanks for consideration
Matilde
Your error comes from indexing a list with [[:
consider
pl = list(qplot(1,1), qplot(2,2))
pl[[1]] returns the first plot, but do.call expects a list of arguments. You could do it with, do.call(grid.arrange, pl[1]) (no error), but that's probably not what you want (it arranges one plot on the page, there's little point in doing that). Presumably you wanted all plots,
grid.arrange(grobs = pl)
or, equivalently,
do.call(grid.arrange, pl)
If you want a selection of this list, use [,
grid.arrange(grobs = pl[1:2])
do.call(grid.arrange, pl[1:2])
Further parameters can be passed trivially with the first syntax; with do.call care must be taken to make sure the list is in the correct form,
grid.arrange(grobs = pl[1:2], ncol=3, top=textGrob("title"))
do.call(grid.arrange, c(pl[1:2], list(ncol=3, top=textGrob("title"))))
library(gridExtra) # for grid.arrange
library(grid)
grid.arrange(pltList[[1]], pltList[[2]], pltList[[3]], pltList[[4]], ncol = 2, main = "Whatever") # say you have 4 plots
OR,
do.call(grid.arrange,pltList)
I wish I had enough reputation to comment instead of answer, but anyway you can use the following solution to get it work.
I would do exactly what you did to get the pltList, then use the multiplot function from this recipe. Note that you will need to specify the number of columns. For example, if you want to plot all plots in the list into two columns, you can do this:
print(multiplot(plotlist=pltList, cols=2))

R - save multiplot to file

I’d really appreciate your help with the following problem. I know several ways to save a single plot to a file. My question is: How do I correctly save a multiplot to a file?
To begin with, I’m not an experienced R user. I use ggplot2 to create my plots, and another thing I should probably mention is that I use the RStudio GUI. Using an example from the R Cookbook, I'm able to create multiple plots in one window.
I would like to save this so-called multiplot to a file (preferably as jpeg), but somehow fail to do this.
I’m creating the multiplot as follows:
##define multiplot function
multiplot <- function(..., plotlist=NULL, cols) {
require(grid)
# Make a list from the ... arguments and plotlist
plots <- c(list(...), plotlist)
numPlots = length(plots)
# Make the panel
plotCols = cols # Number of columns of plots
plotRows = ceiling(numPlots/plotCols) # Number of rows needed, calculated from # of cols
# Set up the page
grid.newpage()
pushViewport(viewport(layout = grid.layout(plotRows, plotCols)))
vplayout <- function(x, y)
viewport(layout.pos.row = x, layout.pos.col = y)
# Make each plot, in the correct location
for (i in 1:numPlots) {
curRow = ceiling(i/plotCols)
curCol = (i-1) %% plotCols + 1
print(plots[[i]], vp = vplayout(curRow, curCol ))
}
}
## define subplots (short example here, I specified some more aesthetics in my script)
plot1a <- qplot(variable1,variable2,data=Mydataframe1)
plot1b <- qplot(variable1,variable3,data=Mydataframe1)
plot1c <- qplot(variable1,variable2,data=Mydataframe2)
plot1d <- qplot(variable1,variable3,data=Mydataframe2)
## plot in one frame
Myplot <- multiplot(plot1a,plot1b,plot1c,plot1d, cols=2)
This gives the desired result. The problem arises when I try to save to a file. I can do this manually in RStudio (using Export -> Save plot as image), but I would like to run everything in a script. I manage to save only subplot1d (which is last_plot()), and not the complete multiplot.
What I’ve tried so far:
Using ggsave
ggsave(filename = "D:/R/plots/Myplots.jpg")
This results in only subplot 1d being saved.
Using jpeg(), print() and dev.off()
jpeg(filename = "Myplot.jpg", pointsize =12, quality = 200, bg = "white", res = NA, restoreConsole = TRUE)
print(Myplot)
dev.off()
This results in a completely white image (just the background I assume). print(Myplot) returns NULL.
Not sure what I’m doing wrong here. My lack of understanding R is the reason I am stuck trying to find a solution. Can anyone explain what I’m doing wrong and perhaps suggest a way to solve my problem(s)?
Its because Myplot is the returned value from your multiplot function, and it returns nothing (its job is to print the graphs). You need to call multiplot with the jpeg device open:
jpeg(filename = "Myplot.jpg", pointsize =12, quality = 200, bg = "white", res = NA, restoreConsole = TRUE)
multiplot(plot1a,plot1b,plot1c,plot1d, cols=2)
dev.off()
should work.
Using the example code (R cookbook), it works for me
png("chickweight.png")
multiplot(p1, p2, p3, p4, cols=2)
dev.off()
And for completeness sake, ggsave does not work as it only saves the last printed ggplot object, which in your case is just the last plot. This is caused by the fact that multiplot creates the plot by drawing the ggplot objects onto different subsets of the total graphics device. An alternative is to create the plot by combining the ggplot objects into one big ggplot object, and then printing the object. This would be compatible with ggsave. This approach is implemented by arrangeGrob in the gridExtra package.

Resources