1

Hi I am trying to create plots, using a for loop and assigning them in my global envirnoment, as follows:

library(ggplot2)
library(patchwork)

datasets::anscombe
#>    x1 x2 x3 x4    y1   y2    y3    y4
#> 1  10 10 10  8  8.04 9.14  7.46  6.58
#> 2   8  8  8  8  6.95 8.14  6.77  5.76
#> 3  13 13 13  8  7.58 8.74 12.74  7.71
#> 4   9  9  9  8  8.81 8.77  7.11  8.84
#> 5  11 11 11  8  8.33 9.26  7.81  8.47
#> 6  14 14 14  8  9.96 8.10  8.84  7.04
#> 7   6  6  6  8  7.24 6.13  6.08  5.25
#> 8   4  4  4 19  4.26 3.10  5.39 12.50
#> 9  12 12 12  8 10.84 9.13  8.15  5.56
#> 10  7  7  7  8  4.82 7.26  6.42  7.91
#> 11  5  5  5  8  5.68 4.74  5.73  6.89


for(i in 1:4){
  var = paste0('p', as.character(i))
  assign(
    x = var,
    value = (ggplot(anscombe, 
                   aes(x=get(paste0('x', as.character(i))), 
                       y=get(paste0('y', as.character(i))))) + geom_point()),
    envir=.GlobalEnv
  )
}

(p1 + p2)/(p3 + p4)

As you can see, all the plots produced are same. But when I use the above same code without for loops, I get intended plots for example: p1 and p3 are shown below.


p1 = ggplot(anscombe, aes(x=get(paste0('x', as.character(1))),
                          y=get(paste0('y', as.character(1))))) + geom_point() 

p3 = ggplot(anscombe, aes(x=get(paste0('x', as.character(3))),
                     y=get(paste0('y', as.character(3))))) + geom_point() 
p1 + p3

Created on 2020-09-06 by the reprex package (v0.3.0)

My question is why isn't the for loop working? Why it's producing the p4 plot for all in the for loop?

monte
  • 1,482
  • 1
  • 10
  • 26
  • 1
    These might be useful https://stackoverflow.com/questions/50929344/multiple-plots-in-for-loop-ignoring-par & https://stackoverflow.com/questions/4856849/looping-over-variables-in-ggplot/52045613#52045613 – Tung Sep 07 '20 at 01:44

1 Answers1

1

You could make this a bit simpler by creating column names and passing them to ggplot, then selectively unquoting them with !!

Note that you're already in the global environment so assign doesn't need the envir argument.

for(i in 1:4) {
  var = paste0('p', i)
  x <- as.name(paste0("x", i))
  y <- as.name(paste0("y", i))
  assign(x = var, value = ggplot(anscombe, aes(!!x, !!y)) + geom_point())
}

(p1 + p2)/(p3 + p4)

enter image description here

Incidentally, you could be even more succinct and avoid writing multiple objects to the global environment if you kept all the plots in a list:

p <- lapply(1:4, function(i) {
  ggplot(anscombe, aes(!!as.name(paste0("x", i)), !!as.name(paste0("y", i)))) + 
    geom_point()
})

(p[[1]] + p[[2]])/(p[[3]] + p[[4]])
Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • I tried a similar way, but using `eval(as.name(paste0('x', i)))`, but again got the same plot for all section. Does it matter if I define column names inside `aes` or outside `ggplot` call? – monte Sep 06 '20 at 11:20
  • @monte `ggplot2` uses non-standard evaluation, so if you want to create names on the fly like this, you you need to pass them as names and unquote them with `!!`. If you use `get` or `eval` you are passing the whole column as data, not the symbolic name of the column, which is what ggplot wants. – Allan Cameron Sep 06 '20 at 11:34
  • Thanks Allan, using `!!` does work, but I am just wondering why does `eval` and `get` works when used individually for each plot, but in `for` loop every previous plot gets overwritten by the last plot. – monte Sep 08 '20 at 05:05