Arrange multiple plots with ggarrange in for loop

Viewed 832

I would like to arrange multiple plots in one image with the following code:

mydataframe <- data.frame(
  number1 = sample(1:140, size=200, replace=TRUE),
  number2 = sample(1:4000, size=200, replace=TRUE),
  number3 = sample(1:30, size=200, replace=TRUE),
  number4 = sample(1:200, size=200, replace=TRUE)
)

unit <- c(
  "number1" = "kmh", 
  "number2" = "rpm", 
  "number3" = "degrees", 
  "number4" = "volts"
)

labels <- c(
  "number1" = "Number 1", 
  "number2" = "Number 2", 
  "number3" = "Number 3", 
  "number4" = "Number 4"
)

for(key in names(unit))
{
  c_plot <- paste0 ("plot_", key) # generate new variable per plot
  
  assign (c_plot, # assign current plot to new variable
          
          ggplot(mydataframe, aes(x=key, y=eval(parse(text = key)))) +
            geom_boxplot() +
            labs (x = labels[key], y = unit[key]) +
            theme(axis.text.x = element_blank())
  )
  
  ggsave(filename = paste0("plots/", key, ".jpg"), plot = eval(parse(text = c_plot))) # save current plot as single image - that works
}

allplots <- ggarrange(plot_number1, plot_number2, plot_number3, plot_number4, # arrange all 4 plots - plot is always the last one generated
                      labels = c("A", "B", "C", "D"),
                      ncol = 2, nrow = 2)

allplots

As a result I get an image with 4 plots (what is correct), the label of x and y axis is correctly assigned (f.e. number1 for x and mph for y axis), but everything else is 4 times the last plot - the same data with same boxplots and the same y scales.

The strange thing is that the images saved with ggsave (which saves the current plot) is performed correctly - every single plot is created with the correct data and is different from all other plots.

Has anyone an idea what I do wrong? It seems that a specific part of the code in the for loop is not unified to the variable "c_plot" and is overwritten at the end of the loop, so in the end the data of the last plot is used for all plots.

2 Answers

I was right. It's a lazy evaluation problem.

The "apply" family of functions, including, sapply, lapply and mapply, apply a function to series of values. See the online help.

Next time, please indicate which packages your code requires so that you provide a self-contained, reproducible example.

library(tidyverse)
library(ggpubr)

plotList <- lapply(
              names(unit),
              function(key) {
                # Need to assign the plot to a variable because 
                # you want to generate the plot AND save to file 
                x <- ggplot(mydataframe, aes(x=key, y=eval(parse(text = key)))) +
                       geom_boxplot() +
                       labs (x = labels[key], y = unit[key]) +
                       theme(axis.text.x = element_blank())
                # No need for the plot argument.  It defaults to the last plot created.
                ggsave(filename = paste0("plots/", key, ".jpg"))
                # Return the plot just created
                x
              }
)

allplots <- ggarrange(plotlist=plotList,
                      labels = c("A", "B", "C", "D"),
                      ncol = 2, nrow = 2)

allplots

Giving

enter image description here

using the approach @Limey

mydataframe <- data.frame(
  number1 = sample(1:140, size=200, replace=TRUE),
  number2 = sample(1:4000, size=200, replace=TRUE),
  number3 = sample(1:30, size=200, replace=TRUE),
  number4 = sample(1:200, size=200, replace=TRUE)
)

unit <- c(
  "number1" = "kmh", 
  "number2" = "rpm", 
  "number3" = "degrees", 
  "number4" = "volts"
)

labels <- c(
  "number1" = "Number 1", 
  "number2" = "Number 2", 
  "number3" = "Number 3", 
  "number4" = "Number 4"
)

library(tidyverse)
library(ggpubr)
plotList <- imap(unit, ~ggplot(mydataframe, aes(x = !!sym(.y))) +
       labs(x = .x, y = labels[.y]) +
       geom_boxplot() +
       coord_flip())

ggarrange(plotlist = plotList, labels = LETTERS[1:4])

Created on 2021-07-14 by the reprex package (v2.0.0)

Related