How to make "persistent" plots with ggplot for ggarrange before and after data manipulation in R

Viewed 324

I am trying to create data comparison plots before-after data manipulation for multiple columns in my dataset via a for loop. Eventually, I want to save all comparison plots to one pdf file. First, I generate the plot before, manipulate the data, generate plot after and want to have them side by side via ggarrange (I also tried grid.arrange from gridExtra, but this does not solve the issue). What I get, however, are identical plots AFTER data manipulation (though the titles are different).

Here is a reproducible example:

library(rlist)
library(ggplot2)
library(ggpubr)
head(iris)
plot_before <-  list()
plot_after <- list()
plots <- list()

for (i in 1:4){
  p <-  ggplot(iris,aes(iris[,i])) + geom_histogram()+ggtitle(paste0(i,"_pre"))
  print(p)
  plot_before <- list.append(plot_before,p)
  #do something with your data
  iris[,i] <- 3*iris[,i]
  p2 <-  ggplot(iris,aes(iris[,i])) + geom_histogram()+ggtitle(paste0(i,"_post"))
  print(p2)
  plot_after <- list.append(plot_after, p2)
  q <-  ggarrange(p,p2)  #here, p is already linked to modified data
  print(q)
  plots <- list.append(plots, q)
}
#try to access plots from lists
for (i in 1:4){
  print(plot_before[[i]])
  print(plot_after[[i]])
  print(plots[[i]])
}

I suppose this has sth to do with that ggplot creates "only" a graphics object linked to the data, so the moment I print it again, it accesses the data again and fetches manipulated data instead of getting a previous "snapshot". Saving the graphs to separate lists also does not help, they are "linked" to manipulated data as well.

Is there a way to make a persistent ggplot object rather than having it linked to the data?

One could, of course create new columns with the modified data and refer to those or create a completely new dataframe, but I would like to avoid data duplication.

1 Answers

The patchwork package helps. An option is to create a list of list of plots, and then flatten the list, and use patchwork::wrap_plots.

A more ggplot way is to avoid using vectors in aes. I've included the way I would create the aes.

update

As per your comment - you don't want to do the data modification twice, and don't want to save extra columns. Now the loop both modifies the data and creates a list of the desired plots.

library(tidyverse)
library(patchwork)

p_list <- list()
sel_col <- names(iris)[1:4]

for(i in sel_col){
  p <-  ggplot(iris, aes(!!sym(i))) +
    geom_histogram()+
    ggtitle(paste0(i,"_pre"))

  p_list[[i]][["pre"]] <- p

  iris[i] <- 3*iris[,i ]

  p2 <- ggplot(iris, aes(!!sym(i))) + 
    geom_histogram()+
    ggtitle(paste0(i,"_post"))

  p_list[[i]][["post"]] <- p2
}

head(iris) # iris has changed
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1         15.3        10.5          4.2         0.6  setosa
#> 2         14.7         9.0          4.2         0.6  setosa
#> 3         14.1         9.6          3.9         0.6  setosa
#> 4         13.8         9.3          4.5         0.6  setosa
#> 5         15.0        10.8          4.2         0.6  setosa
#> 6         16.2        11.7          5.1         1.2  setosa

ls_unnest <- do.call(list, unlist(p_list, recursive=FALSE))

wrap_plots(ls_unnest, ncol = 2)

Created on 2020-05-07 by the reprex package (v0.3.0)

Helpful thread: How to flatten a list of lists?

Related