1

I'd like to dynamically assign a plot to a variable name, and then call that variable latter on within a loop. While using "eval" outside of a loop seems to work just fine, placing it within a loop stops it from working as expected.

#Sample data frame
x<-c(1,2,3,4,5)
y<-c(5,4,3,2,1)
y2<-c(1,2,3,4,5)


DF<-data.frame(x,y,y2)


#Using ggplot for p and p2
p<-ggplot(DF, aes(x=x, y=y))+
            geom_point()
p2<-ggplot(DF, aes(x=x, y=y2))+
  geom_point()

#Assign p and p2 to string "Plot1" and "Plot2"
assign(paste0("Plot",1), p )
assign(paste0("Plot",2), p2 )


#Create a list to hold all plot names
plotlist<-c("Plot1", "Plot2")




#Print plots to a pdf
pdf(paste0("Plot", "_Test.pdf"), height =8, width=16)

for(i in seq(1,length(plotlist))){
  plotname<-plotlist[i]
  plotter<-eval(parse(text=plotname))
  plotter
  print(plotname)

}

dev.off()

Note that the above does not work. But if I am to run the same eval statements outside of the loop, AKA:

  i=1
  plotname<-plotlist[i]
  plotter<-eval(parse(text=plotname))
  plotter

The plot is created as expected. Is there a way to call "eval" within a loop? And what about being in a loop causes eval statement to work differently?

Note, by removing the for loop, it saves the (first) pdf as expected:

pdf(paste0("Plot", "_Test.pdf"), height =8, width=16)

#for(i in seq(1,length(plotlist))){
  plotname<-plotlist[i]
  plotter<-eval(parse(text=plotname))
  plotter
  print(plotname)

#}

dev.off()
Vint
  • 413
  • 6
  • 17
  • 1
    It should be `print(plotter)`, not `print(plotname)` to actually print the plot to the output device rather than just the name (or you can print both). But using `assign` and `eval` isn't really a good pattern in R. You should be storing related items in list and applying functions over those lists rather than looping. It makes everything so much easier. – MrFlick Mar 19 '19 at 20:33
  • The "print" statement was to print out the "plotname" to ensure I was evaluating the correct statement – Vint Mar 19 '19 at 20:44
  • The plot should print from the "plotter" line above the print statement – Vint Mar 19 '19 at 20:45
  • 1
    Well, you need to explicitly `print()` ggplot objects inside of a loop, otherwise they will not render like base graphics plots will. See https://stackoverflow.com/questions/15678261/r-ggplot-does-not-work-if-it-is-inside-a-for-loop-although-it-works-outside-of for example. – MrFlick Mar 19 '19 at 20:46
  • Adding "print(plotter)" does not fix the issue – Vint Mar 19 '19 at 20:49
  • 1
    If I add `print(plotter)` just before `print(plotname)` and run your first code chunk, i get a pdf with both plots. Is there something else you were trying to fix? – MrFlick Mar 19 '19 at 20:52
  • I think MrFlick was correct, that I need to explicitly call "print" for a ggplot object. I assumed it was a problem with the "eval" aspect, but it was an issue with how I was using ggplot – Vint Mar 19 '19 at 21:03

1 Answers1

2

A more R-like way to do this avoiding the assign/eval would be

DF <- data.frame(
  x = c(1,2,3,4,5),
  y = c(5,4,3,2,1),
  y2 = c(1,2,3,4,5))

plotlist <- list(
  Plot1 = ggplot(DF, aes(x=x, y=y)) +
    geom_point(),  
  Plot2 = ggplot(DF, aes(x=x, y=y2)) +
    geom_point()
)

pdf(paste0("Plot", "_Test.pdf"), height =8, width=16)
lapply(plotlist, print)
dev.off()

All your plots here are easily stored in a list that we can just lapply() over when needed.

The main problem is that ggplot objects won't render until they are print()ed. When you work in the console, by default the result of the last expression is print()ed. But when you run a loop, that default print()ing doesn't happen. That's described in this previous question: R: ggplot does not work if it is inside a for loop although it works outside of it

MrFlick
  • 195,160
  • 17
  • 277
  • 295