3

I have 15 plots that I need to display, which I iterate through in a for list like so:

### Plotting that does not work
plots <- c()
for (i in colnames(dataframeselect)){
  current_col <- dataframeselect[i]
  plots <- c(plots, ggplot(dataframeselect, aes_string(colnames(current_col))) + geom_histogram())
}
ggarrange(plotlist = plots)

While I can iterate and create plots individually I can't manage to create a plot list, that I can then pass to ggarrange.

I now have to resort to create 15 variables, which gets the job done fine but is somewhat tedious and not DRY-friendly:

### Plotting that works
my_plot <- function(column) ggplot(dataframeselect, aes_string(x = column)) + geom_histogram()

p1 <- my_plot("W3_f15771g")
p2 <- my_plot("W3_f15771a")
p3 <- my_plot("W3_f15771b")
#...
ggarrange(p1,p2,p3)

Please note that there is a question already being asked, but none of those answers use a for loop in order to get the desired result: Lay out multiple ggplot graphs on a page

The warning that I get is the following:

  Cannot convert object of class FacetNullFacetggprotogg into a grob.
44: In as_grob.default(plot) :
  Cannot convert object of class environment into a grob.
45: In as_grob.default(plot) : Cannot convert object of class list into a grob.
46: In as_grob.default(plot) :
  Cannot convert object of class tbl_dftbldata.frame into a grob.
47: In as_grob.default(plot) : Cannot convert object of class list into a grob.
48: In as_grob.default(plot) :
  Cannot convert object of class ScalesListggprotogg into a grob.
49: In as_grob.default(plot) : Cannot convert object of class uneval into a grob.
50: In as_grob.default(plot) : Cannot convert object of class list into a grob.

What is interesting is that, whenever I add a new plot to my list, the count of the objects does not increment by 1 but by nine elements. So by looking at the plots variable, I see that it is a big mess not of some plot objects but of all the fragments from all the plots I wanted to add:

messed-up-data-soup

So I am wondering how to somehow put the plot inside some sub-container that it then can be used by ggarrange.

Besi
  • 22,579
  • 24
  • 131
  • 223
  • Try `plots <- list()` – akrun Nov 10 '19 at 20:53
  • @akrun I did try this. How should I then append to this list. I did try `append` and `plots = c(plots, ggplot..` but neither of them worked. – Besi Nov 10 '19 at 20:55
  • Relevant: [Save multiple ggplots using a for loop](https://stackoverflow.com/questions/26034177/save-multiple-ggplots-using-a-for-loop) – markus Nov 10 '19 at 21:12
  • Related: https://stackoverflow.com/a/52045613/786542 – Tung Nov 11 '19 at 01:32

3 Answers3

3

One option is to initialize the plots as a list with length same as the number of columns of dataset

library(ggplot2)
plots <-  vector('list', ncol(dataframeselect))
for (i in seq_along(dataframeselect)){
  current_col <- colnames(dataframeselect)[i]
  plots[[i]] <- ggplot(dataframeselect, aes_string(current_col)) + 
                                      geom_histogram()
 }
akrun
  • 874,273
  • 37
  • 540
  • 662
  • 1
    It is so not R, but indeed, I couldn't find a way around neither with `c(plot_list1, plot_list2)`, nor smth around `list(plot_list1[[1]], plot_list1[[2]], ...)` as they all fail under `ggarrange`. R classes are so weird, this is where I miss Python terribly. – JelenaČuklina Jul 03 '20 at 08:29
3

How about a simple lapply instead of a for loop? Along the lines of

library(ggpubr)
library(tidyverse)

make_plot <- function(n) {
  tibble(x = rnorm(n), y = rnorm(n)) %>%
    ggplot(aes(x = x, y = y)) +
    geom_point()
}

plots <- lapply(1:15, make_plot)

ggarrange(plotlist = plots)

Which produces

enter image description here

MSR
  • 2,731
  • 1
  • 14
  • 24
1

If you are going to use ggarrange() it sounds like you you could make use of facet_wrap. The main thing is to make the dataset long first:

library(ggplot2)
library(tidyr)

 as.data.frame(volcano[, 1:10])%>%
  pivot_longer(everything())%>%
  ggplot(aes(value)) + 
  geom_histogram() + 
  facet_wrap(vars(name))

enter image description here

A similar approach in base would be:

DF <- as.data.frame(volcano[, 1:10])
grps <- names(DF)

#make hists without plotting
hists <- lapply(DF, hist, plot = F)

#plot limits:
x_min <- min(DF)
x_max <- max(DF)

y_max <- max(unlist(lapply(hists, '[[', 'counts'), use.names = F))

#try to make it rectangular:
n_by_n <- ceiling(sqrt(length(grps)))
par(mfrow = c(if (n_by_n * (n_by_n-1) < length(grps)) n_by_n else n_by_n-1, n_by_n))

#loops through the elemnts
mapply(function(x, grp) plot(x, main = grp, xlim = c(x_min, x_max), ylim = c(0, y_max)),
       hists,
       grps)

base facet histogram

Cole
  • 11,130
  • 1
  • 9
  • 24