Save multiple plots in loop in R - 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"))
}

Related

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)

for loop issue in ggplot2

I would like to output multiple graphs from ggplot2. I found very nice examples but still could not find what I want to achieve.
r-saving-multiple-ggplots-using-a-for-loop
by using similar example, I only want to output three different graphs for each species so far I am outputting same three combined graph.
library(dplyr)
fill <- iris%>%
distinct(Species,.keep_all=TRUE)
plot_list = list()
for (i in 1:length(unique(iris$Species))) {
p = ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width)) +
geom_point(size=3, aes(colour=Species))
geom_rect(data=fill,aes(fill = Petal.Width),xmin = -Inf,xmax = Inf,ymin = -Inf,ymax = Inf,alpha = 0.5)+ ## added later to OP
plot_list[[i]] = p
}
# Save plots to tiff. Makes a separate file for each plot.
for (i in 1:3) {
file_name = paste("iris_plot_", i, ".tiff", sep="")
tiff(file_name)
print(plot_list[[i]])
dev.off()
}
What I am missing?
You are not filtering the dataset inside the for-loop, and so the same graph is repeated three times without change.
Note the changes here:
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))
plot_list[[i]] = p
}
Addressing the OP update, this works for me:
fill <- iris %>%
distinct(Species, .keep_all=TRUE)
for (i in unique(iris$Species)) {
p = ggplot(iris[iris$Species == i, ], aes(x=Sepal.Length, y=Sepal.Width)) +
geom_rect(data = fill, aes(fill = Petal.Width),xmin = -Inf,xmax = Inf,ymin = -Inf,ymax = Inf,alpha = 0.5) +
geom_point(size = 3, aes(colour = Species))
plot_list[[i]] = p
}

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.

ggplot line or point plotting conditionally

I'm using ggplot to write a bunch of plots inside a function. I want to pass another flag to the function so that I can choose while calling the function that whether to plot lines or points.
Currently I'm doing it like this:
plot2pdfHD = function(opdata, dir = 'plots'){
#... do something ...
plots <- list()
for (i in seq(strikes)){
#... do something ...
plots[[i]] <- ggplot(sset, aes(x = TIMESTAMP, y = value, col = optype)) +
geom_line() + labs(x = "Time", y = 'values') +
#... do something ...
}
pdf(paste0(dir, '/', Sys.Date(), '.pdf'), width=16, height=10)
for(i in seq(length(plots)))
tryCatch({print(plots[[i]])}, error = function(e) NULL)
dev.off()
}
I want to add a flag so that by setting appropriate value to the flag I can switch between geom_line() and geom_point() while calling the function.
Addition:
Can it be done without repeating the additional call part, i.e. #... do something ...? I am hoping for an answer that does that.
Also sset is a subset of the opdata.
Maybe this is what you're looking for? I like #arvi1000's answer---nice and clear---but you can put the if statement inside a single ggplot addition expression:
type = "line"
## also try with
# type = "point"
ggplot(mtcars, aes(x = wt, y = mpg)) + {
if(type == "point") geom_point() else geom_line()
} +
theme_bw()
For multiple layers, you could do something like this:
gg = ggplot(mtcars, aes(x = wt, y = mpg))
{
if(type == "point") {
gg + geom_point()
} else {
gg + geom_line() + stat_smooth()
}
} + theme_bw()
(Of course, adding the theme_bw() to the original gg definition would be cleaner, but this demonstrates that it can be added later.)
Plain old if block?
plot2pdfHD = function(opdata, dir = 'plots', plot_type){
plots <- list()
for (i in seq(strikes)) {
# base gg call
p <- ggplot(sset, aes(x = TIMESTAMP, y = value, col = optype))
# add geom layer based on content of argument:
if(plot_type == 'line') p <- p + geom_line()
if(plot_type == 'point') p <- p + geom_point()
# add additional params and assign into list item
plots[[i]] <- p + labs(x = "Time", y = 'values')
#...
}
# ...
}
Other notes:
I'm assuming you are doing something to make sset different before each call, otherwise you are going to get a list of identical plots
lapply might be better than a for loop here, esp since you are wanting a list object as the result anyway

Resources