Search code examples
rlistggplot2ggpubr

storing ggplot graphs from if/if else loops in a list, and using ggarrange


Explanation of the problem

I have extensive if, else if, loops that produce graphs in ggplot2. Each graph gets stored with a unique object name, with the following pattern:

    graph_log_bar_blue <- 
    graph_log_violin_blue <-
    graph_log_bar_red <-
    graph_log_violin_red <-
    graph_log_bar_green <-
    graph_log_violin_green <-

etc...

I wish to store the graphs that are created (the ones that satisfy the loop conditions), in a list, so that I can arrange them with either ggarrange (in ggpubr) or with gridExtra.

graph_list <- ls(pattern = "graph_") #list names of graphs (but doesn't store the graph in the list)

library("ggpubr")
ggarrange(graph_list,
          nrow = 1, ncol = 3)

# OR

library("gridExtra")                                              # Load gridExtra 
do.call("grid.arrange", c(plot_list, ncol = 3)) 

Question

How do I:

  1. Create a list of graphs produced by ggplot (is this possible?)
  2. How can I select/make a list of graphs produced inside an if/else loop?
  3. How do I call graphs produced to then arrange them as a panel figure using ggarrange etc (ggpubr package)?

DATA

using the iris data set. The if loops do not make logical sense, it is just a mock of the structure of loop I am using, take it with a grain of salt :/

library(ggplot2)
iris <- iris
#Dataset 1
kt <- kruskal.test(Petal.Width ~ Species, data = iris)
kt$p.value
if(kt$p.value > 0.05){
  pt <- pairwise.wilcox.test(iris$Petal.Width, g=iris$Species, p.adjust.method = "BH")
}else if(kt$p.value < 0.05){
  graph_log_bar_blue <- ggplot(data = iris, aes(y=Petal.Width, x=Species, fill = Species)) +
    stat_summary(fun = median, show.legend = FALSE, geom="crossbar") +
    geom_jitter(show.legend = FALSE, width = 0.25, shape = 21, colour = "black", size = 2.5) +
    labs(x=NULL, y= "Petal Width (cm)")
}else{
  graph_log_violin_blue <- ggplot(data = iris, aes(y=Petal.Width, x=Species, fill = Species)) +
    geom_violin(show.legend = FALSE, trim = FALSE, adjust = 0.6) +
                  labs(x=NULL,
                       y= "Petal Width (cm)")
}


#dataset 2
kt_sep <- kruskal.test(Sepal.Width ~ Species, data = iris)
kt_sep$p.value
if(kt_sep$p.value > 0.05){
  pt_sep <- pairwise.wilcox.test(iris$Sepal.Width, g=iris$Species, p.adjust.method = "BH")
}else if(kt_sep$p.value > 0.05){
  graph_log_bar_red <- ggplot(data = iris, aes(y=Sepal.Width, x=Species, fill = Species)) +
    stat_summary(fun = median, show.legend = FALSE, geom="crossbar") +
    geom_jitter(show.legend = FALSE, width = 0.25, shape = 21, colour = "black", size = 2.5) +
    labs(x=NULL, y= "Sepal Width (cm)")
}else{
  graph_log_violin_red <- ggplot(data = iris, aes(y=Sepal.Width, x=Species, fill = Species)) +
    geom_violin(show.legend = FALSE, trim = FALSE, adjust = 0.6) +
    labs(x=NULL,
         y= "Sepal Width (cm)")
}

graph_list <- ls(pattern="graph_")

Where do I go from here to arrange both graphs together!


Solution

  • You could create your plot_list using e.g.

    plot_list <- lapply(grep("^graph_", ls(), value = TRUE), get)
    

    Afterwards you could pass the list to the plotlist argument of ggpubr::ggarrange, i.e.

    ggpubr::ggarrange(plotlist = plot_list)
    

    A minimal reprex of this approach using some fake plots:

    library(ggplot2)
    library(ggpubr)
    
    graph_log_bar_blue <- ggplot()
    graph_log_violin_blue <- ggplot()
    
    plot_list <- lapply(grep("^graph_", ls(), value = TRUE), get)
    
    ggpubr::ggarrange(plotlist = plot_list)
    

    However, instead of storing your graphs as single objects I would suggest to create a list of plots from start. Additionally, instead of using copy and paste to create your charts it might be worthwhile to write a small custom function to perform your tests and create the charts. Such an approach may look like so:

    library(ggplot2)
    library(ggpubr)
    
    plot_fun <- function(.data, x, y, ylab) {
      kt <- kruskal.test(.data[[y]] ~ .data[[x]])
      
      if (kt$p.value > 0.05) {
        pt <- pairwise.wilcox.test(.data[[y]], g = .data[[x]], p.adjust.method = "BH")
        plot <- NULL
      } else {
        pt <- NULL
        base <- ggplot(.data, aes(.data[[x]], .data[[y]], fill = .data[[x]])) +
          labs(x = NULL, y = ylab)
        
        plot <- if (kt$p.value < 0.05) {
          base +   
            stat_summary(fun = median, show.legend = FALSE, geom="crossbar") +
            geom_jitter(show.legend = FALSE, width = 0.25, shape = 21, colour = "black", size = 2.5)
        } else {
          base + 
            geom_violin(show.legend = FALSE, trim = FALSE, adjust = 0.6)
        }
      }
      
      # Return test results and plot as list
      list(kt = kt, pt = pt, plot = plot)
    }
    
    p_list <- list(
      plot_fun(iris, "Species", "Petal.Width", "Petal Width (cm)"),
      plot_fun(iris, "Species", "Sepal.Width", "Sepal Width (cm)")
    )
    
    # Get the plots
    plots <- lapply(p_list, function(x) x[["plot"]])
    
    ggpubr::ggarrange(plotlist = plots)