Saving ggplot graphs to pdf not formatting correctly in PDF - r

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()

Related

Plot ggplots stored in list over several pages

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.

tikz produces empty file in R loop

I am using the tikzDevice library in R to produce tikzpicture plots in latex. It works when plots are produced one by one. However, when I want to produce the plots in a loop, I get empty files. This issue is illustrated in the following minimal example:
library(ggplot2)
library(tikzDevice)
df <- data.frame(
x = c(1, 2),
y = c(1, 2)
)
path <- 'some path here'
for (j in 1:25){
filename <- paste(path, j, sep = '')
filename <- paste(filename, '.tex', sep = '')
tikz(file = filename)
plot <-
ggplot(data=df, aes(x=x, y=y))
plot
dev.off()
}
This produces 25 empty plots. However, if I run the code without the for loop (for instance, copying the content in the loop 25 times and changing j accordingly, then it produces the plots. What is the problem here?
plot(plot) or one loses the plot

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

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
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.
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:

Printing multiple ggplots into a single pdf, multiple plots per page

I have a list, p, where each element of p is a list of ggplot2 plotting objects.
I would like to output a single pdf containing all the plots in p such that the plots in p[[1]] are on page 1, the plots in p[[2]] are on page 2, etc. How might I do this?
Here's some example code to provide you with the data structure I'm working with--apologies for the boring plots, I picked variables at random.
require(ggplot2)
p <- list()
cuts <- unique(diamonds$cut)
for(i in 1:length(cuts)){
p[[i]] <- list()
dat <- subset(diamonds, cut==cuts[i])
p[[i]][[1]] <- ggplot(dat, aes(price,table)) + geom_point() +
opts(title=cuts[i])
p[[i]][[2]] <- ggplot(dat, aes(price,depth)) + geom_point() +
opts(title=cuts[i])
}
This solution is independent of whether the lengths of the lists in the list p are different.
library(gridExtra)
pdf("plots.pdf", onefile = TRUE)
for (i in seq(length(p))) {
do.call("grid.arrange", p[[i]])
}
dev.off()
Because of onefile = TRUE the function pdf saves all graphics appearing sequentially in the same file (one page for one graphic).
Here is the most elegant solution to exporting a list of ggplot objects into a single pdf file using ggplot2::ggsave() and gridExtra::marrangeGrob().
library(ggplot2)
library(gridExtra)
Let's say you create multiple plots using lapply()
p <- lapply(names(mtcars), function(x) {
ggplot(mtcars, aes_string(x)) +
geom_histogram()
})
Save list of p plots:
ggsave(
filename = "plots.pdf",
plot = marrangeGrob(p, nrow=1, ncol=1),
width = 15, height = 9
)
Here is a simpler version of Sven's solution for the R beginners who would otherwise blindly use the do.call and nested lists that they neither need nor understand. I have empirical evidence. :)
library(ggplot2)
library(gridExtra)
pdf("plots.pdf", onefile = TRUE)
cuts <- unique(diamonds$cut)
for(i in 1:length(cuts)){
dat <- subset(diamonds, cut==cuts[i])
top.plot <- ggplot(dat, aes(price,table)) + geom_point() +
opts(title=cuts[i])
bottom.plot <- ggplot(dat, aes(price,depth)) + geom_point() +
opts(title=cuts[i])
grid.arrange(top.plot, bottom.plot)
}
dev.off()
I've tried some of these solutions but with no success. I researched a little more and found a solution that worked perfectly for me. It saves all my graphics in a single pdf file, each chart on one page.
library(ggplot2)
pdf("allplots.pdf",onefile = TRUE)
for(i in glist){
tplot <- ggplot(df, aes(x = as.factor(class), y = value))
print(tplot)
}
dev.off()
Here's one solution, but I don't particularly like it:
ggsave("test.pdf", do.call("marrangeGrob", c(unlist(p,recursive=FALSE),nrow=2,ncol=1)))
The problem is that it relies on there being the same number of plots in each group. If all(sapply(p, length) == 2) were false, then it would break.
A solution that worked for me with ggpubr package (package on Github, code for installation: devtools::install_github("kassambara/ggpubr")).
Let's say you have 4 plots p1, p2, p3 and p4.
library(ggpubr)
multi.page <- ggarrange(p1,p2,p3,p4, nrow=1, ncol=1) # for one plot per page
multi.page[[1]] # for seeing the first plot
ggexport(multi.page, filename="my-plots.pdf")
More examples of ggpubr use: http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/81-ggplot2-easy-way-to-mix-multiple-graphs-on-the-same-page/
Here's a function based on Sven's approach, including the roxygen2 documentation and an example.
#' Save list of ggplot2 objects to single pdf
#'
#' #param list (list) List of ggplot2 objects.
#' #param filename (chr) What to call the pdf.
#'
#' #return Invisible NULL.
#' #export
#'
#' #examples
#' #plot histogram of each numeric variable in iris
#' list_iris = map(names(iris[-5]), ~ggplot(iris, aes_string(.)) + geom_histogram())
#' #save to a single pdf
#' GG_save_pdf(list_iris, "test.pdf")
GG_save_pdf = function(list, filename) {
#start pdf
pdf(filename)
#loop
for (p in list) {
print(p)
}
#end pdf
dev.off()
invisible(NULL)
}
A nice solution without the gridExtra package:
library(plyr)
library(ggplot2)
li = structure(p, class = c("gglist", "ggplot"))
print.gglist = function(x, ...) l_ply(x, print, ...)
ggsave(li, file = "test.pdf")

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