11

I wish to evaluate a vector of strings containing arithmetic expressions -- "1+2", "5*6", etc.

I know that I can parse a single string into an expression and then evaluate it as in eval(parse(text="1+2")).

However, I would prefer to evaluate the vector without using a for loop.

foo <- c("1+2","3+4","5*6","7/8") # I want to evaluate this and return c(3,7,30,0.875)
eval(parse(text=foo[1])) # correctly returns 3, so how do I vectorize the evaluation?
eval(sapply(foo, function(x) parse(text=x))) # wrong! evaluates only last element
C8H10N4O2
  • 18,312
  • 8
  • 98
  • 134
  • 1
    How is using `sapply` is vectorizing? – David Arenburg Jul 26 '14 at 20:28
  • @DavidArenburg Because he is operating on multiple elements of a vector at one time? – stanekam Jul 26 '14 at 20:30
  • @iShouldUseAName, `sapply` is the same thing as `for` loop, just slower. For such simple operation, a `for` loop will be a better choice. In `R`, this is not what you mean by saying "vectorized solution" – David Arenburg Jul 26 '14 at 20:32
  • Scratch that. Same time. – stanekam Jul 26 '14 at 20:38
  • @iShouldUseAName, no, it's not. I tested it too and for loop wins. Compare this to your `sapply`: `for(i in seq_along(foo)){ eval(parse(text = foo[i])) }` – David Arenburg Jul 26 '14 at 20:41
  • @DavidArenburg I'm getting about 1.5 seconds for both my loop, your loop and the sapply for 40000 iterations. I'll expand it and see if any difference starts developing. – stanekam Jul 26 '14 at 20:49
  • @DavidArenburg plus your for loop isn't returning a value so it isn't really a good comparison, is it? You're not assigning it to anything, saving a lot of time. – stanekam Jul 26 '14 at 20:52
  • @iShouldUseAName, you aren't assigning your `sapply` to anything neither, but you could add `print` there or something. Anyhow, I think I made my point clear so we can close this – David Arenburg Jul 26 '14 at 20:54
  • @DavidArenburg That's fine but `sapply` is returning a value, the intended value in a vector. Your loop doesn't. – stanekam Jul 26 '14 at 20:57
  • @iShouldUseAName, I told you already, add `print` – David Arenburg Jul 26 '14 at 20:58
  • @DavidArenburg you're not understanding my point. `print` doesn't return a value. It just prints the values. You would need to make your loop assign the values to a vector to match the effect and have it act as a solution to the question. No one wants their values printed to the screen. They want them in an object so they can use them. What your for loop does is calculate the result and leave it be. – stanekam Jul 26 '14 at 21:00
  • @iShouldUseAName, Ok, so how is it different from your `sapply`? As it stands, it is only printing the output to the screen – David Arenburg Jul 26 '14 at 21:06
  • @DavidArenburg because `sapply` returns a vector? – stanekam Jul 26 '14 at 21:07
  • @iShouldUseAName, I agree that for a vector of around 100K length, `foo <- sapply(foo, function(x) eval(parse(text=x)))` will be very slightly more efficient than `for(i in seq_along(foo)) foo[i] <- eval(parse(text = foo[i]))`, but I don't think this is the case here and it still won't be a vecorized solution – David Arenburg Jul 26 '14 at 21:10
  • @DavidArenburg except that all vectorization is is abstracting away the loop so it is vectorized. – stanekam Jul 26 '14 at 21:15
  • @iShouldUseAName, `sapply` is not abstracting away the loop, it is just hiding it – David Arenburg Jul 26 '14 at 21:21
  • @DavidArenburg haha then what is vectorization to you? What would abstracting away the loop be? You do realize that when you add two vectors in r that there is a for loop written in C or Fortran that does it, right? – stanekam Jul 26 '14 at 21:28
  • @iShouldUseAName, that is what I exactly mean. a C or Fortran loop - is vectorized, a hidden R loop - is not. See [here](http://stackoverflow.com/questions/5533246/why-is-apply-method-slower-than-a-for-loop-in-r) – David Arenburg Jul 26 '14 at 21:30
  • @iShouldUseAName, or even better, read [R Inferno](http://www.burns-stat.com/pages/Tutor/R_inferno.pdf) page 24 – David Arenburg Jul 26 '14 at 21:38
  • @DavidArenburg for what is worth. Vectorization is not really specific to a language. Hiding an R loop, or a C loop is still vectorization. Vectorization doesn't make any implication that it would be faster. – Andrei Feb 14 '22 at 17:13
  • @Andrei I'm not going to enter an almost a decade old discussion- but I meant vectorization as in my previous comment. Or even better as it defined [here](https://stackoverflow.com/questions/1422149/what-is-vectorization) "*Many CPUs have "vector" or "SIMD" instruction sets which apply the same operation simultaneously to two, four, or more pieces of data.*". This is definitely implying it would be faster that a by element loop. In Rs case, creating by row loops in an R data.frame, for instance, would be slower (though more memory friendly) than a compiled function such `rowSums` or such. – David Arenburg Feb 14 '22 at 18:10

2 Answers2

11

Just apply the whole function.

sapply(foo, function(x) eval(parse(text=x)))
stanekam
  • 3,906
  • 2
  • 22
  • 34
  • 1
    I am accepting this because it works, thank you very much! However, to David Arenburg's point, I do understand that there is a difference between `sapply` and a vectorized operation. Perhaps "how do I serialize this" would have been a better comment. It appears that `?eval` takes only a single expr input, not a vector, and it is beyond my r abilities to vectorize that. – C8H10N4O2 Jul 26 '14 at 21:41
  • @JohnAndrews, you could just stay with your loop and get over with it – David Arenburg Jul 26 '14 at 21:42
  • 8
    @JohnAndrews `eval` does evaluate multiple expressions, but it treats them as a single compound expression for the purpose of returning a value. Eg, try `eval(parse(text=c("a <- 1", "b <- 2")))`. You will find objects `a` and `b` in your workspace. – Hong Ooi Jul 27 '14 at 04:23
  • But ```parse``` is vectorized, so I have an even better one, ```sapply(parse(text=foo), eval)```, or perhaps even better ```sapply(str2expression(foo), eval)```. Feel free to include it in your answer if you wish. – Andrei Feb 14 '22 at 17:06
2

Just to show that you can also do this with a for loop:

result <- numeric(length(foo))
foo <- parse(text=foo)
for(i in seq_along(foo))
    result[i] <- eval(foo[[i]])

I'm not a fan of using the *apply functions for their own sake, but in this case, sapply really does lead to simpler, clearer code.

Hong Ooi
  • 56,353
  • 13
  • 134
  • 187