0

I have to consecutively plot lots of variables against a fixed one using ggplot2 in R. For doing so, I wrote a for cycle generating the code lines like this:

l <- "ggplot(data = db)+geom_line(aes(x = asofdate, y = copper), colour = 'tomato')+geom_line(aes(x = asofdate, y = db[['"
x <- vector()
for (j in (3:ncol(db))){
     n <- colnames(db[,j])
     k <- paste0(n, "']]), colour = 'blue')+labs(title = 'copper & ", n,"'); ")
     x <- paste0(x,l,k)
}
eval(parse(text=substr(x, 1, (nchar(x)-1))))

Where in the for cycle I conclude each graph generation with a semicolon, so that R recognizes groups of code to be evaluated separately. At this stage the code is stored into a string. Then I use the function parse and eval to evaluate the string and execute it as code.

However, after I run the for cycle, in x I have that each graph code is separated by a semicolon ; while after using the function parse the semicolons become commas ,.

This makes my for cycle useless, since R does not recognize different groups of code.

Any idea why this happens and how can I keep the semicolon after parse?

MichaelChirico
  • 33,841
  • 14
  • 113
  • 198
Vitomir
  • 295
  • 2
  • 14

1 Answers1

1

There's no need to use semicolons; parse would accept newlines between commands, e.g.:

eval(parse(text='print(3+3)\nprint(4+4)'))
# [1] 6
# [1] 8

Further, you might consider the following cleaner version of your code:

code_fmt = "
ggplot(data = db) + 
  geom_line(aes(x = asofdate, y = copper), colour = 'tomato') + 
  geom_line(aes(x = asofdate, y = db[['%s']]), colour = 'blue') +
  labs(title = 'copper & \"%s\"')
"
plots = sapply(tail(colnames(db), -2L), function(nm) {
  parse(text = sprintf(code_fmt, nm, nm))
})
eval(plots)

Using code_fmt allows us to cleanly write what the full expression will look like, properly indented and everything. You could also replace the %s with e.g. {nm} and replace the values with the glue package rather than sprintf.

tail(x, -n) returns elements (n+1):length(x), i.e., excluding the first n elements

sapply here will return an expression(...); eval(expression(...)) evaluates the elements in sequence, e.g. consider:

eval(expression(print(3+3), print(4+4)))
# [1] 6
# [1] 8

Further further, I don't see why you need eval(parse in the first place -- what's wrong with a simple loop?

for (nm in tail(colnames(db), -2L)) {
  ggplot(data = db) + 
    geom_line(aes(x = asofdate, y = copper), colour = 'tomato') + 
    geom_line(aes(x = asofdate, y = db[[nm]]), colour = 'blue') +
    labs(title = sprintf('copper & \"%s\"', nm))
}
MichaelChirico
  • 33,841
  • 14
  • 113
  • 198
  • Thank you very much, absolutely great explanation and I very much agree with everything. However, when I try the solution with code_fmt only the last plot is generated. While when I try the last solution without eval(parse), no plot at all is printed. Do you know why and how this could be avoided? Many thanks again! – Vitomir Dec 08 '19 at 10:09