Multiple ggplots in one page using for Loop in R - r

I am trying to plot multiple "ggplot" in the same page using for loop. The for loop is used because the number of the plots is non-determined "dynamic". The x-axis of the plots will be changed within each iteration. I read so many articles about creating an empty list then add each plot in specific index of the list, then use "multiplot" function to display all the plots of the list in one page. However, This not working !!
The problems is the program ends-up by printing only the last plot information saved at the last index of the list with different labels! the code and the figure below indicates the idea.
`
Howmany <- readline(prompt="Specify the number of the independent variables: ")
Howmany <- as.numeric(Howmany)
plot_lst <- vector("list", length = Howmany) #' an empty list
for ( i in 1:Howmany){
plot_lst[[i]] <- ggplot(data=data, aes(x=data[, c(i)], y=data$gender)) +
geom_point(aes(size = 5)) +
scale_color_discrete(name = "dependent_variable" + labs(
title = (paste("Logistic Regression Fitting Model",i)),
x = names(data)[i],
y = "gender")
}
multiplot(plotlist = plot_lst, cols = 1)
I really appreciate any suggestion.
I also tried what were suggested in this link: show multiple plots from ggplot on one page in r
However, still am facing the same problem.

Because ggplot's aes() is using lazy evaluation you need to force evaluation in each iteration of the loop (otherwise all plots will be the same on the last position of i).
One way to do this is by wrapping the righthand side of the assignment in local() and use i <- i:
The labs(x = ...) seemed not to be correct so I rewrote it as:
x = names(data)[i], please check if that works for you.
plot_lst <- vector("list", length = Howmany) #' an empty list
for (i in 1:Howmany) {
plot_lst[[i]] <- local({
i <- i
ggplot(data=data, aes(x=data[, c(i)], y=data$gender)) +
geom_point(aes(size = 5)) +
scale_color_discrete(name = "dependent_variable") +
labs(
title = (paste("Logistic Regression Fitting Model", i)),
x = names(data)[i],
y = "gender")
})
}
Below is one example using the iris data set. If we print plot_lst we can see three different plots.
I assume the function multiplot is from the scatter package, which is not working with the latest R version, so I can't reproduce if this is working correctly.
Howmany <- readline(prompt="Specify the number of the independent variables: ")
Howmany <- as.numeric(Howmany)
plot_lst <- vector("list", length = Howmany) #' an empty list
for ( i in 1:Howmany){
plot_lst[[i]] <- local({
i <- i
ggplot(data = iris,
aes(x = iris[, c(i)],
y = iris$Species)) +
geom_point(aes(size = 5)) +
scale_color_discrete(name = "dependent_variable") +
labs(
title = paste("Logistic Regression Fitting Model", i),
x = names(data)[i],
y = "species"
)
})
}
plot_lst

Related

Changing aesthetics in ggplot generated by svars package in R

I'm using the svars package to generate some IRF plots. The plots are rendered using ggplot2, however I need some help with changing some of the aesthetics.
Is there any way I can change the fill and alpha of the shaded confidence bands, as well as the color of the solid line? I know in ggplot2 you can pass fill and alpha arguments to geom_ribbon (and col to geom_line), just unsure of how to do the same within the plot function of this package's source code.
# Load Dataset and packages
library(tidyverse)
library(svars)
data(USA)
# Create SVAR Model
var.model <- vars::VAR(USA, lag.max = 10, ic = "AIC" )
svar.model <- id.chol(var.model)
# Wild Bootstrap
cores <- parallel::detectCores() - 1
boot.svar <- wild.boot(svar.model, n.ahead = 30, nboot = 500, nc = cores)
# Plot the IRFs
plot(boot.svar)
I'm also looking at the command for a historical decomposition plot (see below). Is there any way I could omit the first two facets and plot only the bottom three lines on the same facet?
hist.decomp <- hd(svar.model, series = 1)
plot(hist.decomp)
Your first desired result is easily achieved by resetting the aes_params after calling plot. For your second goal. There is probably an approach to manipulate the ggplot object. Instead my approach below constructs the plot from scratch. Basically I copy and pasted the data wrangling code from vars:::plot.hd and filtered the prepared dataset for the desired series:
# Plot the IRFs
p <- plot(boot.svar)
p$layers[[1]]$aes_params$fill <- "pink"
p$layers[[1]]$aes_params$alpha <- .5
p$layers[[2]]$aes_params$colour <- "green"
p
# Helper to convert to long dataframe. Source: svars:::plot.hd
hd2PlotData <- function(x) {
PlotData <- as.data.frame(x$hidec)
if (inherits(x$hidec, "ts")) {
tsStructure = attr(x$hidec, which = "tsp")
PlotData$Index <- seq(from = tsStructure[1], to = tsStructure[2],
by = 1/tsStructure[3])
PlotData$Index <- as.Date(yearmon(PlotData$Index))
}
else {
PlotData$Index <- 1:nrow(PlotData)
PlotData$V1 <- NULL
}
dat <- reshape2::melt(PlotData, id = "Index")
dat
}
hist.decomp <- hd(svar.model, series = 1)
dat <- hd2PlotData(hist.decomp)
dat %>%
filter(grepl("^Cum", variable)) %>%
ggplot(aes(x = Index, y = value, color = variable)) +
geom_line() +
xlab("Time") +
theme_bw()
EDIT One approach to change the facet labels is via a custom labeller function. For a different approach which changes the facet labels via the data see here:
myvec <- LETTERS[1:9]
mylabel <- function(labels, multi_line = TRUE) {
data.frame(variable = labels)
}
p + facet_wrap(~variable, labeller = my_labeller(my_labels))

saving list of ggplots generated using pwalk

With the amazing help of #Tung we have created a function that creates a list of ggplots through a loop using purrr::pwalk. However, the ploblem is that the plots are printed automatically and it is not possible (or I am not able to solve the problem) to save them as a list of plots. I am coming from this post: Passing labels to xlab and ylab in ggplot2
NOTE: I need to change the ylab and xlab labels from each plot.
The function to plot is as follows:
library(tidyverse)
plot_scatter_with_label <- function(dat,
var_x,
var_y,
label_x,
label_y,
geom_smooth = FALSE,
point_shape = 16,
point_color = "#EB3300",
point_size = 1,
point_alpha = 1,
smooth_method = "loess",
smooth_se = FALSE,
smooth_color = "navy") {
if (is.character(var_x)) {
print('character column names supplied, use rlang::sym()')
var_x <- rlang::sym(var_x)
} else {
print('bare column names supplied, use dplyr::enquo()')
var_x <- enquo(var_x)
}
if (is.character(var_y)) {
var_y <- rlang::sym(var_y)
} else {
var_y <- enquo(var_y)
}
p <- ggplot(dat, aes(x = !! var_x, y = !! var_y)) +
geom_point(shape = point_shape, color = point_color,
size = point_size, alpha = point_alpha) +
ylab(label_y) +
xlab(label_x) +
ggtitle(paste0(label_x, " ~ ", label_y))
print(p)
}
Create a data frame so that we can loop through every row and column
var_y = c("mpg", "hp")
label_y = c("Miles per gallon [Mpg]", "Horse power [CV]")
var_x = c("cyl", "gear")
label_x = c("Cylinders [n]", "Gear [n]")
var_xy <- expand.grid(var_x, var_y, stringsAsFactors = FALSE)
label_xy <- expand.grid(label_x, label_y, stringsAsFactors = FALSE)
select_dat <- data.frame(var_xy, label_xy, stringsAsFactors = FALSE)
pwalk(select_dat, ~ plot_scatter_with_label(mtcars, ..1, ..2,..3,..4))
The problem is that using pwalk and I am guessing that due to the print(p) from the function plot_scatter_with_label, the plots are automatically displayed. Instead, I would like to save them in a list of plots. For example:
I would like:
p_list = pwalk(select_dat, ~ plot_scatter_with_label(mtcars, ..1, ..2,..3,..4))
where p_list is a list of plots to "play with" using some function to arrange them like
cowplot::plot_grid(plotlist=p_list, nrow=3,ncol=2)
or
ggpubr::ggarrange(plotlist=p_list, nrow=3,ncol=2)
#Tung has recomended me to have a look at this post: Multiple plots in for loop ignoring par
However, I am still unable to find the solution.
Any help will he highly appreciated.
Thanks a lot in advance,
Best regards,
Juan Antonio
Edit:: corrected spelling of function plotlist
The pwalk function is explicitly designed not to return an output, but instead to focus on side-effects (like printing, reading/writing, or plotting). It is an alternative function to pmap, which does return its output.
You could return the plots in a list like this:
Change the last line of your custom function from print(p) to return(p)
Use the pmap function rather than pwalk

Using the "grep" function to Create Y axis label for boxplots in ggplot2

I created a loop to make 57 boxplots that uses grep to pick out the variables I want. However, for whatever reason the Y axis is always labeled the first variable it grabs despite the fact that it grabs all 57 unique variables and creates the loop. I was wondering if someone could take a look at it to see how I could get the Y axis to correspond to what the data actually is. Below is my code.
labely = names(ivf_dataset)[grep("_DMR_", names(ivf_dataset), ignore.case = T, fixed = TRUE)] #grabbing first only why?
myboxplot <- function(mydata=ivf_dataset, myexposure, myoutcome ) {
bp <- ggplot(mydata, aes(x = as.factor(get(myexposure)), y =
get(myoutcome))) +
geom_boxplot()
print(bp)
}
# test one variable
myboxplot(myexposure = "ART_CURRENT", myoutcome = "H19_DMR_mean")
# creating loop for 57 boxplots
outcomes = names(ivf_dataset)[grep("_DMR_", names(ivf_dataset), ignore.case = T)] #pulls out dmr names
allplots <- list()
for (i in seq_along(outcomes)) {
allplots[[i]]<- myboxplot (myexposure = "ART_CURRENT", myoutcome =
outcomes[i])$plot + xlab("ART_CURRENT") +
ylab(labely)
}
allplots

Creating a grid of ggplot graphs from a list using gridExtra

I'm trying to create an automated graphing function and have a few questions about laying out graphs from lists in ggplot2 using grid.arrange
Here's toy data
set.seed(1234)
df <- data.frame(rep(NA,20))
for (i in 1:6) df[,i] <- rnorm(20, 0, 3)
names(df) <- paste("a", 1:6, sep = "")
Now create a simple graphing function with a contingency based on presence or absence of an argument, in this case whether or not a note is added to the graph (note: I am doing it this way to represent my real workflow as closely as possible)
gFunct <- function (vec, note = NULL) {
if(is.null(note)) {
g <- ggplot(df, aes_string(vec)) + geom_histogram()
return(g)
} else {
g <- ggplot(df, aes_string(vec)) + geom_histogram() + annotate("text", x = 0, y = 3, label = note)
return(g)
}
}
Now create two lists
nameList <- names(df)
noteList <- list(a1 = NULL, a2 = "hey", a3 = "hey there", a4 = NULL, a5 = NULL, a6 = "there")
Now pass those lists into the graphing function via the foreach function
library(foreach)
gg <- foreach(x = nameList, y = noteList) %do% gFunct(x,y)
What we get when we call gg is a list of graphs, indexed by [[idx]]. I want to know two things
(1) How to pass these into grid.arrange without needing to turn them into grobs in the graphing function. At the moment I'm getting an error message saying only 'grobs' allowed in "gList"but on other occasions I have not needed to convert ggplot2 graphs to grobs to pass them into grid.arrange.
(2) Leaving aside the grob problem, is there any way to pass the whole list into grid.arrange without having to list every graph manually? In other words I would like to be able to create the matrix of graphs with
gridExtra::grid.arrange(gg, ncol = 3)
rather than
gridExtra::grid.arrange(gg[[1]], gg[[2]], gg[[3]], gg[[4]], gg[[5]], gg[[6]], ncol = 3)
gridExtra::grid.arrange(grobs = gg, ncol = 3)

Combining uneven numbers of plots with a for loop and grid.arrange

I have hundreds of plots of that I have made with a previous for loop that are named in a consistent manner. The plots are the results of several tasks nested within domains.
I would like to use grid.arrange to plot all of the tasks of a defined domain on the same plot.
Dummy data that explains the structure of my data and plots:
domain_key <- data.frame(domain = c("soe", "soe", "soe", "elit", "elit"),
tasks = c("personal", "size", "shapeid", "onetoone", "puzzle"))
dummy <- ggplot(data.frame()) + geom_point() + xlim(0, 10) + ylim(0, 100)
plot.personalpct <- dummy + ggtitle("plot.personalpct")
plot.sizepct <- dummy + ggtitle("plot.sizepct")
plot.shapeidpct <- dummy + ggtitle("plot.shapeidpct")
plot.onetoonepct <- dummy + ggtitle("plot.onetoonepct")
plot.puzzlepct <- dummy + ggtitle("plot.puzzlepct")
And here is my basic idea of how to do it:
for(j in domain_key$domain){
tasks <- unique(with(domain_key, tasks[domain == j])) #Get a list of the unique tasks for the domain
plots <- paste("plot.", tasks, "pct", sep ="") #Get the name of the plots as a character vector
grid.arrange(eval(parse(text = plots))) #evaluate the expression with grid arrange to display all the plots
}
My problem is that the final argument only displays the first plot of each domain. This is because my character vector doesn't parse as mulitple objects, probably because they are not separated by a comma. I've tried a bunch of workarounds, but can't figure out a way around this. Or maybe my approach is totally off.
Much appreciate any assistance.
Maybe this helps
library(ggplot2)
library(gridExtra)
dummy_plot <- function(id, fill) ggplot() + ggtitle(id) +
theme(panel.background = element_rect(fill = fill))
pl = list(`domain 1` = lapply(1:3, dummy_plot, fill = "#FBB4AE"),
`domain 2` = lapply(1:2, dummy_plot, fill = "#B3CDE3"),
`domain 3` = lapply(1:4, dummy_plot, fill = "#CCEBC5"),
`domain 4` = lapply(1:5, dummy_plot, fill = "#DECBE4"))
dummy_group <- function(domain) arrangeGrob(grobs = pl[[domain]], top = domain)
grid.arrange(grobs = lapply(names(pl), dummy_group))
Thanks for the help of #baptiste and #lukeA. Both of their suggestions didn't quite answer my question, but helped put me on the right path.
I realized that I really needed to feed grid.arrange a list, so I found a reasonable, but not terribly elegant solution. I embedded the domain name in the plot name for each task. Then I fed grid.arrage and list of plots using grep with the domain name. This works reasonably well.
This has a bit too much detail but illustrates my solution:
### Graph means of tasks by treatment group with error bars
pltList <- list() #Create and empty list to store task plots
for(outcome in unique(domain_key$task) ){
df <- data.frame(Intervention.Group = factor(unique(children$treatment)),
Percent.Correct = eval(parse(text = paste0("meansbytreatment$", outcome, "$estimates"))),
SE = eval(parse(text = paste0("meansbytreatment$", outcome, "$se"))))
df$upper <- df$Percent.Correct + 2*df$SE
df$lower <- df$Percent.Correct - 2*df$SE #Make a temp df with with the estimates and errors for each treatment group
domain <- unique( domain_key$domain[domain_key$task == outcome] ) #extract the domain that the task belongs to
pltName <- paste( "plot", outcome, domain, sep = "." ) #Make a unique plot name with task and domain
pltList[[ pltName ]] <- ggplot(df, aes(Intervention.Group, Percent.Correct, fill = Intervention.Group)) +
geom_bar(stat = "identity") +
geom_errorbar(aes(ymin = lower, ymax = upper), width = 0.4) +
ggtitle(paste(outcome, "by Intervention Group") )+
theme(legend.position="none") #Save the plot to the plot list
}
### Graph domain subtasks together
Domainplt <- lst() #Make an empty list to store plots of all the taks in a domain
for(j in unique(domain_key$domain)){
plots <- grep(j, names(pltList)) #get all of the plots from the domain
nplots <- length(plots) # get the n of plots for the domain
Domainplt[[j]] <- grid.arrange(grobs = pltList[plots],
ncol = nplots,
top = j) #Arrange all of the plots horizontally
}

Resources