Save a list of plots from environment in R - r

I want to save all my plots from my R environment with ggsave(). How can I save list of plots from environment in R and then use the list as input to ggsave() ?
I got some plots with cars from here to illustrate:
PlotA <- ggplot(mtcars, aes(x=hp, y=mpg, color=cyl, shape=cyl)) +
geom_point(size=3)
PlotB <- ggplot(mtcars, aes(x=hp, y=mpg, color=cyl, shape=cyl)) +
geom_point(size=3) +
geom_smooth(method="lm", aes(fill=cyl))
PlotC <- ggplot(mtcars, aes(x=hp, y=mpg)) +
geom_point(size=3, aes(color=cyl, shape=cyl)) +
geom_smooth(method="loess", color="black", se=FALSE) +
geom_smooth(method="lm", aes(color=cyl, fill=cyl))
My attempt:
saveplots <- list()
saveplots <- ls(pattern = 'Plot')
### Save pngs ###
for(i in 1:length(saveplots)){
ggsave(saveplots[[i]],
file=paste0("Total", saveplots,".png"),
width = 22, height = 11.5, units = "cm",
path = "plots/")
}
Some posts helped a bit, but not quite yet (ex1 ex2). Any ideas? Thanks in adv.

You can use the function get to get the object from the environment.
for(i in 1:length(saveplots)){
ggsave(plot = get(saveplots[[i]]),
filename=paste0("Total", saveplots[[i]],".png"),
width = 22, height = 11.5, units = "cm",
path = "plots/")
}

Using mget
library(purrr)
library(dplyr)
library(stringr)
mget(savePlots) %>%
iwalk(~ ggsave(str_c("Total", .y, ".png"),
.x, width = 22, height = 11.5, units = "cm", path = "plots/"))

Another similar way using lapply with ls and get:
plots_list <- lapply(ls(pattern="Plot"), get)
lapply(seq_along(plots_list), function(i) {
ggsave(paste0("Total", i, ".png"), plots_list[[i]], width=22, height=11.5, units="cm", path = "plots/")
})

Alternative if you don't know/remember/care what the names of all your ggplots are, try saveplots <- lsclass('ggplot') . It's available in my package (at CRAN) "cgwtools" ; source provided here:
lsclass <- function (type = "numeric")
{
inlist <- ls(.GlobalEnv)
classlist <- sapply(1:length(inlist), function(j) class(get(inlist[j])))
tnams <- sapply(1:length(inlist), function(j) type %in% classlist[[j]])
return(inlist[tnams])
}

Related

Save multiple plots in loop in R

I want to save multiple plots as png using a loop. Unfortunately the loop doesn't seem to work, as the saving and plotting of one single plot works but if I do it within the loop, nothing is saved. I don't receive a error message either. Just nothing happens.
Here is the code that works:
name = main_emo[i]
mypath=file.path("/Users/Jasmin/Documents/Psychologie/Master/Masterarbeit/Working directory", name)
png(mypath)
qplot(x_emo,y_emo, main = main_emo[i]) + geom_errorbar(aes(x=x_emo, ymin=y_emo-sd, ymax=y_emo+sd), width=0.25) + ylab("Rating") + xlab("Emotion")+
theme(panel.grid.major = element_blank()) + scale_y_continuous(breaks=seq(1,7,1)) + expand_limits(y=7)
dev.off()
and this is the loop where it doesn't work anymore:
main_emo <- c("Emotion Profile Funeral", "Emotion Profile Wedding", "Emotion Profile Destroyed Street","Emotion Profile Destroyed Street")
frame_name <- c("nice", "wedd", "des", "fun")
emo_mean <- c("rmean_EM_Trauer_", "rmean_EM_Freude_","rmean_EM_Angst_", "rmean_EM_Aerger_", "rmean_EM_Ekel_", "rmean_EM_Ueber_")
for (i in 1: length(frame_name)) {
y_emo <- c()
sd <- c()
x_emo <- c("Trauer", "Freude", "Angst", "Aerger", "Ekel", "Üeberraschung")
for (j in 1: length(emo_mean)) {
y_col <- unlist(pre_sub[colnames(pre_sub) == paste0(emo_mean[j], frame_name[i])], use.names=FALSE)
y_emo <- c(y_emo, mean(y_col, na.rm=TRUE))
sd <- c(sd, sd(y_col, na.rm=TRUE))
}
name = main_emo[i]
mypath=file.path("/Users/Jasmin/Documents/Psychologie/Master/Masterarbeit/Working directory", name)
png(mypath)
qplot(x_emo,y_emo, main = main_emo[i]) + geom_errorbar(aes(x=x_emo, ymin=y_emo-sd, ymax=y_emo+sd), width=0.25) + ylab("Rating") + xlab("Emotion")+
theme(panel.grid.major = element_blank()) + scale_y_continuous(breaks=seq(1,7,1)) + expand_limits(y=7)
dev.off()
}
Thanks for your help!
Use ggsave()
Doesn't work:
library("ggplot2")
for (i in unique(diamonds$color)) {
png(paste0("~/Desktop/colour_", i, ".png"))
ggplot(diamonds, aes(carat, price)) + geom_point()
dev.off()
}
Does work:
for (i in unique(diamonds$color)) {
ggplot(diamonds, aes(carat, price)) + geom_point()
ggsave(paste0("~/Desktop/color_", i, ".png"))
}

saving each modified facet in ggplot2

I try to save each Species data in iris data set to .png file using for loop. But before, that I would like to modify facet strip thickness as I needed to do in my real data plotting process.
However, when I attempted to write each facet
the following code below it just giving me the empty plots for each of these Species.
Here is my attempt,
library(ggplot2)
plot_list = list()
for (i in unique(iris$Species)) {
p = ggplot(iris[iris$Species == i, ], aes(x=Sepal.Length, y=Sepal.Width)) +
geom_point(size=3, aes(colour=Species))+
facet_wrap(~Species)
#this part to modify facet_wrap strips
g1 = ggplotGrob(p)
pos = c(unique(subset(g1$layout, grepl("panel", g1$layout$name), select = t)))
for(i in pos) g1$heights[i-1] = unit(0.4,"cm")
grobs = which(grepl("strip", g1$layout$name))
for(i in grobs) g1$grobs[[i]]$heights <- unit(1, "npc")
grid.newpage()
grid.draw(g1)
plot_list[[i]] = g1
}
#finally write the modified graphs to file
for (i in 1:3) {
file_name = paste("iris_plot_", i, ".png", sep="")
tiff(file_name)
print(plot_list[[i]])
dev.off()
}
Currently this code is producing the empty graphs and do not know why! Any help will be appreciated!
You don't need to modify strip height using ggplotGrob. Setting the relevant parameter in ggplot's theme() would do:
p1 = ggplot(iris[iris$Species == "setosa",],
aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
facet_wrap(~Species)
p2 = p1 + theme(strip.text.x = element_text(margin = margin(t = 10, b = 10)))
# note: default margin for top & bottom is 5.5
gridExtra::grid.arrange(p1, p2, ncol = 2)
As for the rest, you may wish to check the length of plot_list after the first loop. You initially assigned i to take on the unique values of iris$Species, then tried to use it as index for the list of plots. The first three elements of plot_list did not contain plots.
The following would work in this example. You probably need to make some modifications for the actual use case:
plot_list = list()
loop.list <- unique(iris$Species)
for (i in seq_along(loop.list)) {
p = ggplot(iris[iris$Species == loop.list[i], ],
aes(x = Sepal.Length, y=Sepal.Width)) +
geom_point(size = 3, aes(colour = Species))+
facet_wrap(~Species) +
theme(strip.text.x = element_text(margin = margin(t = 11, b = 11)))
plot_list[[i]] <- ggplotGrob(p)
}
for (i in 1:3) {
file_name = paste("iris_plot_", i, ".png", sep="")
tiff(file_name)
grid.draw(plot_list[[i]])
dev.off()
}

R ggplot2 for loop plots same data

I have put together a simple for loop to generate a series of plots and then use grid.arrange to plot them. I have two problems:
The axes of the plots change correctly to the column names, but the same data is plotted on each graph. Having put in a breakpoint and stepped through the code it appears to be incrementing correctly so I'm not sure why.
I have set the plot aesthetic to group on year, however this produces intermediate .5 years that appear in the legend. This hasn't happened to me before.
Should all be reproducible using mtcars.
library(ggplot2)
library(gridExtra)
result <- mtcars
for(i in 1:2) {
nam <- paste("p", i, sep = "")
assign(
nam, ggplot(result, aes(x = disp, y = results[i+4], group = gear, color = gear)) +
geom_line() +
geom_point() +
scale_colour_distiller(palette = "Dark2", direction = -1, guide = "legend") +
scale_y_continuous(name = colnames(results[i+4])) +
scale_x_continuous(name = "x")
)
}
plist <- mget(paste0("p", 1:2))
do.call(grid.arrange, plist)
I think trying to access the columns by their number in the aes mapping is confusing ggplot. This works:
for(i in 1:2) {
nam <- paste("p", i, sep = "")
assign(
nam, ggplot(result,aes_string(x="disp",y=colnames(result)[i+4], group="gear", color="gear")) +
geom_line() +
geom_point() +
scale_colour_distiller(palette = "Dark2", direction=-1, guide="legend") +
scale_y_continuous(name=colnames(result[i+4])) +
scale_x_continuous(name="x")
)
}
I would suggest iterating over the names though; this makes the code much clearer. Here's a version that does this and skips the detour around the environment:
plots <- lapply(c("drat", "wt"), function(column) {
ggplot(result,aes_string(x="disp",y=column, group="gear", color="gear")) +
geom_line() + geom_point() +
scale_colour_distiller(palette = "Dark2", direction=-1, guide="legend") +
scale_y_continuous(name=column) +
scale_x_continuous(name="x")}) %>%
do.call(grid.arrange, .)
do.call(grid.arrange, plots)
Your using results and result. And you should use aes_string and then refer to the variables by string name:
You should also avoid to make tons of assignments. Just put it all into a list()
library(ggplot2)
library(gridExtra)
result<-mtcars
for(i in 1:2) {
nam <- paste("p", i, sep = "")
assign(
nam, ggplot(result,aes_string(x="disp",y=names(result)[i+4], group="gear", color="gear")) +
geom_line() +
geom_point() +
scale_colour_distiller(palette = "Dark2", direction=-1, guide="legend") +
scale_y_continuous(name=colnames(result[i+4])) +
scale_x_continuous(name="x")
)
}
plist <- mget(paste0("p", 1:2))
do.call(grid.arrange, plist)
The problem is that the plot is generated in the for loop, but evaluated in the do.call. Since i has changed in the for loop, both are evaluated with i = 2. You can confirm this with:
i <- 3
do.call(grid.arrange, plist)
A small adjustment to your code fixes the issue:
for(i in 1:2) {
nam <- paste("p", i, sep = "")
coln <- colnames(result[i+4])
assign(
nam, ggplot(result,aes_(x=~disp,y=as.name(coln), group=~gear, color=~gear)) +
geom_line() +
geom_point() +
scale_colour_distiller(palette = "Dark2", direction=-1, guide="legend") +
scale_y_continuous(name=coln) +
scale_x_continuous(name="x")
)
}
plist <- mget(paste0("p", 1:2))
do.call(grid.arrange, plist)
You should take full advantage of ggplot::facet_wrap
This means tidying your data to a single data frame that's interpretable to ggplot
Data
temp <- mtcars
Tidy data
library(purrr)
library(dplyr)
Names <- map_chr(1:2, ~names(temp)[.x+4])
# "drat" "wt"
data <- map_df(1:2, ~temp[,c("cyl", names(temp)[.x+4])] %>% setNames(c("cyl", "value")), .id="iteration") %>%
mutate(iteration = Names[as.numeric(iteration)])
plot with facet_wrap
ggplot(data=data, aes(x=cyl, y=value, label=iteration)) +
geom_line() +
geom_point() +
facet_wrap(~iteration)

can't open pdf plot r

For some reason, in this loop the PDFs that it produces end up corrupt. However, when I plot each individually it is saved and I can open them. Please advise, going mad!
for (l in 1:length(which_genes)) {
gene_name <- which_genes[[l]]
cases_values <- cases[cases$HGNC == genes[gene_name],]
controls_values <- controls[controls$HGNC == genes[gene_name],]
t <- t.test(cases_values[c(2:ncol(cases_values))], controls_values[c(2:ncol(controls_values))])
case <- cbind(t(cases_values[c(2:ncol(cases_values))]), "cases")
cont <- cbind(t(controls_values[c(2:ncol(controls_values))]), "controls")
dat <- as.data.frame(rbind(case, cont))
names(dat) <- c("expression", "type")
dat$expression <- as.numeric(dat$expression)
#plot significant genes
pdf(file = paste(genes[gene_name], "_different.pdf", sep=""))
ggplot(dat, aes(type, expression, fill=type)) +
geom_boxplot() +
ggtitle(paste(genes[gene_name], "pvalue", t$p.value)) +
xlab("cases vs controls")
dev.off()
}
Yet another instance of the failure-to-print error (as described in the R-FAQ). Use this instead inside the loop:
pdf(file = paste(genes[gene_name], "_different.pdf", sep=""))
print( ggplot(dat, aes(type, expression, fill=type)) +
geom_boxplot() +
ggtitle(paste(genes[gene_name], "pvalue", t$p.value)) +
xlab("cases vs controls")
)
dev.off()
If the goal was to have a multi-page output then you should have opened the PDF-device outside the loop, print-ed within the loop, and then closed the device outside.

How do I overlay an image on to a ggplot?

I'd like to read an image from the web. e.g.
http://api.altmetric.com/donut/502878_64x64.png
and insert it into the top right of a ggplot
df <- data.frame(x=1:10, y=sample(1:100,10))
# a fake plot to try it on.
ggplot(df, aes(x,y)) + geom_point(size = 2)
How would I do this?
You are looking for annotation_raster and readPNG
mypngfile <- download.file('http://api.altmetric.com/donut/502878_64x64.png', destfile = 'mypng.png', mode = 'wb')
library(png)
mypng <- readPNG('mypng.png')
p <- qplot(mpg, wt, data = mtcars) + theme_bw()
p + annotation_raster(mypng, ymin = 4.5,ymax= 5,xmin = 30,xmax = 35) +
geom_point()
These new features (and more examples) are described here
The correct solution was this:
# This was one of my issues, reading a png from the web
my_image <- readPNG(getURLContent('http://path.to/image.png'))
p1 + annotation_raster(my_image, ymin = 4,ymax= 5,xmin = 30,xmax = 40)
Just adding an update from the terrific package Magick, which will even allow a GIF to be overlaid on ggplot images:
library(ggplot2)
library(magick)
library(here) # For making the script run without a wd
library(magrittr) # For piping the logo
# Make a simple plot and save it
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point() +
ggtitle("Cars") +
ggsave(filename = paste0(here("/"), last_plot()$labels$title, ".png"),
width = 5, height = 4, dpi = 300)
# Call back the plot
# Now call back the plot
background <- image_read(paste0(here("/"), "Cars.png"))
# And bring in a logo
logo_raw <- image_read("https://i.imgur.com/e1IneGq.jpg")
frames <- lapply(logo_raw, function(frame) {
image_composite(background, frame, offset = "+70+800")
})
animation <- image_animate(image_join(frames))
image_write(animation, "~/Cars_Travolta.gif")

Resources