4

I would like to have multiple ggplots plots in a single plot. ISSUE: When trying to create a list of plots (to be used later in grid.arrange) using a for-loop, the list is returned empty.
I used these two posts:

1)create figures in a loop
2)use grid.arrange to save in a single fig

to come up with the following code (simpler version)* to plot probability density curves:

#models = 33 obs of 1 variable
plotlist = list()
for (i in 1:33)
{
 modname = models$col1[i]
 p<- ggplot() + geom_line(aes(xi,yi)) + geom_line(aes(ai,bi)) + 
     ggtitle(modname) ## the x,y,a,b are just illustrative.
 #In reality, each pair is produced using fitdist and dgamma functions for
  # data (single column) from separate .csv
 ggsave(outpath)
 plotlist[[i]] = p   

}
main <- grid.arrange(grobs=plotlist,ncol=6)
main
ggsave("bigplot.png",p)

ISSUE (further explanation): plotlist shows up as an empty list. As a result, grid.arrange just plots the subplot created in the last loop 33 times. But, what is strange is that the grid.arrange plot has the correct title for all subplots (assigned using modname)!!! In the attached picture, you will see that all subplots are the same except for the title. Since I save individual subplots as well, I know that the issue is not with the data/code for subplots. main I am fairly new to R and this is my first ggplot2 (* excuse the multiple geom_line()). As a result, it has taken me a while to figure out how to fit distributions and plot them (Thanks, stackoverflow!!) in the first place. So, any help here will be much appreciated.

Update: I was able to accomplish the above using PIL package in python . However, I would really like to be able to do this in R.

Community
  • 1
  • 1
geog_newbie
  • 185
  • 4
  • 15
  • Post the final plot then too. – Mike Wise Apr 04 '17 at 22:18
  • Actually pretty sure my solution will fix it, don't use the `do.call`, use the `grobs=` argument to `grid.arrange`, capture its return result into a variable and use it as the second parameter of `ggsave` – Mike Wise Apr 04 '17 at 22:20
  • And if that does not work try and repro with my little toy example. – Mike Wise Apr 04 '17 at 22:22
  • Ok - I see what you mean, now. But then where do you change the assignment of `x`, `y`, `x1`, and `y1` being used in `ggplot`? If you don't change those then how can you expect the graphics that are drawn to change? I can see why `modname` changed - you changed it explicitly, but not the data for the plot. – Mike Wise Apr 04 '17 at 22:46
  • Where are `x`, `y`, `x1`, and `y1` defined? It seems that no data frame is passed to `ggplot()`. – Uwe Apr 04 '17 at 22:48
  • Oh yes! They are assigned differently. I just posted a simplified code to keep it clean, but not used to doing that.. – geog_newbie Apr 05 '17 at 01:48
  • I can post my real code with some data if any one has time to look into that. But, I think the fact that each loop saves an individual plot that looks exactly like it is supposed to is proof that my ggplot2 function works as expected. Plus, when I check the 'environment' window, it shows `plotlist` as a Null list – geog_newbie Apr 05 '17 at 01:51
  • Sure, go ahead and post the whole thing. – Mike Wise Apr 05 '17 at 06:58
  • There is still no data.frame being used in your `ggplot` call, and you are now not changing `xi`,`yi`, `ai`, and `bi` for each plot creation. So the data in your plots will still not change. – Mike Wise Apr 05 '17 at 07:01
  • Getting anywhere? – Mike Wise Apr 06 '17 at 16:03

2 Answers2

2

If I have understood you correctly (and I am not sure), this is simply about saving your plot files.

I think the problem is simply that grid.arrange() does not work with last_plot(), which is what ggsave apparently uses. So it is best to be explicit about what plot you want it to save.

Here is a simple working example, with less plots and no regression models, just a few random plots:

library(ggplot2)
library(gridExtra)
plotlist = list()
n <- 100
for (i in 1:9)
{
  df <- data.frame(x=rnorm(n),y=rnorm(n))
  pname <- paste0("Plot-",i)
  p<- ggplot(df) + geom_point(aes(x,y)) + ggtitle(pname)
  ggsave(paste0(pname,".png"),p)
  plotlist[[i]] = p   
}
p <- grid.arrange(grobs=plotlist,ncol=6)
ggsave("bigplot.png",p)

The text output:

Saving 4.76 x 5.28 in image
Saving 4.76 x 5.28 in image
Saving 4.76 x 5.28 in image
Saving 4.76 x 5.28 in image
Saving 4.76 x 5.28 in image
Saving 4.76 x 5.28 in image
Saving 4.76 x 5.28 in image
Saving 4.76 x 5.28 in image
Saving 4.76 x 5.28 in image
> p <- grid.arrange(grobs=plotlist,ncol=3)
> ggsave("bigplot.png",p)
Saving 4.76 x 5.28 in image

And the final plot:

enter image description here

Mike Wise
  • 22,131
  • 8
  • 81
  • 104
  • No, the issue is that my plotlist object is returning empty when using the same code you provided in the for-loop. grid.arrange works fine - but, it is not getting the correct list of plots. I will edit the question to focus on that... – geog_newbie Apr 04 '17 at 22:16
  • reproducing your code gives me this error - `Error in unit(rep(1, nrow), "null") : 'x' and 'units' must have length > 0` P.S. thanks for the help! – geog_newbie Apr 05 '17 at 01:58
  • What does "reproduce" mean? If I copy and paste it into R-studio it works fine for me. But if you have certain kinds of mistakes that cause the loop to not execute, but continue to execute after the loop (like a bad file name for `ggsave`), then `plotlist`will be `NULL`, and you will get that error message. – Mike Wise Apr 05 '17 at 06:58
2

the problem with your plotlist is probably that you call the i placeholder in your call to ggplot inside your for loop directly-

ggplot() + geom_line(aes(xi,yi)) + geom_line(aes(ai,bi))

i then gets evaluated lazily when the plot is rendered, i.e., the last value of i is used to create all the plots.

If that is the case, you can use aes_string in stead of aes:

ggplot(df, aes_string(x="your_x", y=i)) + geom_line()

BUT what you are trying to do seems to be a perfect job for facet_grid or facet_wrap, in which case your loop is only used to generate the data:

models <- c("foo","bar","thing")
plotlist <- list()
for (i in 1:length(models))
{
   #xvar, yvar, avar, bvar come from wherever you get them
  plotlist[[i]] <- data.frame(modname = models[i], 
                             xvar = c(1:10), 
                             yvar = rnorm(10), 
                             avar = c(1:10),
                             bvar = rnorm(10, mean = 1),
                             stringsAsFactors = FALSE)   

}

alldata <- do.call(rbind, plotlist)

ggplot(alldata, aes(xvar, yvar)) + 
  geom_line() + 
  geom_line(aes(avar,bvar)) +
  facet_grid(~modname)
Janna Maas
  • 1,124
  • 10
  • 15