372

I'm curious to know if R can use its eval() function to perform calculations provided by e.g. a string.

This is a common case:

eval("5+5")

However, instead of 10 I get:

[1] "5+5"

Any solution?

5
  • 8
    Despite all the answers showing how to solve that with parse ... Why do you need to store language types in a character string ? Martin Mächler's answer should deserve much more upvotes. Commented Nov 29, 2016 at 10:48
  • 8
    Thank you @PetrMatousu. Yes, I'm shocked to see how mis-information is spread on SO now.. by people upvoting eval(parse(text = *)) fake solutions. Commented Apr 1, 2017 at 16:22
  • 2
    I want to run scrips of the form: QQ = c('11','12','13','21','22','23'), i.e.: QQ =c(...,'ij',..) with i,j varying on a range that is may vary from run to run. For this and similar examples, I can write the script as paste( "QQ = c('", paste(rep(1:2,each=3),1:3, sep="", collapse="','"), "')",sep=""), and the option eval(parse(text=...)) creates the vector QQ in the working environment as per the script. What would be the proper R coder way to do this, if not with "text=..."? Commented Sep 18, 2018 at 15:04
  • 4
    @MartinMächler how is eval(parse(text = "5+5")) a "fake solution"? It seems to work fine for all the cases I have tried. There are reasons that one might need to evaluate something read-in as a string. I am finding your answer more confusing and less useful to evaluating a string (OP) than the others, which might be why the other answers have more upvotes? Commented Jan 11, 2022 at 19:56
  • @VictorZurkowski: With your example, written in a "generalizable way", i <- rep(1:2, each=3) ; j <- 1:3 the proper solution is (many times faster and more readable) QQ <- paste0(i, j) -- voilà, that's all. Commented Jan 18, 2022 at 16:22

8 Answers 8

528

The eval() function evaluates an expression, but "5+5" is a string, not an expression. Use parse() with text=<string> to change the string into an expression:

> eval(parse(text="5+5"))
[1] 10
> class("5+5")
[1] "character"
> class(parse(text="5+5"))
[1] "expression"

Calling eval() invokes many behaviours, some are not immediately obvious:

> class(eval(parse(text="5+5")))
[1] "numeric"
> class(eval(parse(text="gray")))
[1] "function"
> class(eval(parse(text="blue")))
Error in eval(expr, envir, enclos) : object 'blue' not found

See also tryCatch.

Sign up to request clarification or add additional context in comments.

5 Comments

As Shane notes below, "You need to specify that the input is text, because parse expects a file by default"
the side-effects of using eval(parse) should be specified. For example, if you have a pre-defined variable name equal to "David" and you reassign using eval(parse(text = "name") == "Alexander", you will get an error because eval & parse do not return an R expression that can be evaluated.
@NelsonGon: Unevaluated expressions constructed using quote(), bquote(), or the more sophisticated tools provided by the rlang package.
@ArtemSokolov Thanks, I somehow keep coming back to this question looking for an alternative. I've looked at rlang but the closest I found was parse_expr which calls parse_exprs which in turn is the same as using parse and wrapping it in eval which seems to be the same thing as done here. I am unsure what the advantage would be of using rlang.
@NelsonGon: with rlang, you would work directly with expressions, not strings. No parse step necessary. It has two advantages. 1. Expression manipulations will always produce valid expressions. String manipulations will only produce valid strings. You won't know if they are valid expressions until you parse them. 2. There's no equivalent to the substitute() class of functions in the string world, which severely limits your ability to manipulate function calls. Consider this glm wrapper. What would a string equivalent look like?
123

You can use the parse() function to convert the characters into an expression. You need to specify that the input is text, because parse expects a file by default:

eval(parse(text="5+5"))

2 Comments

> fortunes::fortune("answer is parse") If the answer is parse() you should usually rethink the question. -- Thomas Lumley R-help (February 2005) >
@MartinMächler That's ironic, because the core R packages use parse all the time! github.com/wch/r-source/…
64

Sorry but I don't understand why too many people even think a string was something that could be evaluated. You must change your mindset, really. Forget all connections between strings on one side and expressions, calls, evaluation on the other side.

The (possibly) only connection is via parse(text = ....) and all good R programmers should know that this is rarely an efficient or safe means to construct expressions (or calls). Rather learn more about substitute(), quote(), and possibly the power of using do.call(substitute, ......).

fortunes::fortune("answer is parse")
# If the answer is parse() you should usually rethink the question.
#    -- Thomas Lumley
#       R-help (February 2005)

Dec.2017: Ok, here is an example (in comments, there's no nice formatting):

q5 <- quote(5+5)
str(q5)
# language 5 + 5

e5 <- expression(5+5)
str(e5)
# expression(5 + 5)

and if you get more experienced you'll learn that q5 is a "call" whereas e5 is an "expression", and even that e5[[1]] is identical to q5:

identical(q5, e5[[1]])
# [1] TRUE

11 Comments

could you give an example? maybe you could show us how to "hold on" to 5+5 in an r object, then evaluate it later, using quote and substitute rather than a character and eval(parse(text=)?
I may be a little lost. At what point do you get 10? Or is that not the point?
eval(quote()) does work in a few cases but will fail for some cases where eval(parse()) would work well.
eval(quote("5+10*2")) returns "5+10*2" whereas eval(parse(text="5+10*2")) returns 25. In this case, quote doesn't seem to be the better function to use here...
@MartinMächler thanks for the clarification. It seems that this doesn't exactly answer the OP then. I understand that you are saying not to use strings at all, but one doesn't always have a choice. If you are handed a dataset that has character strings of equations, how does one evaluate them? There must be an acceptable way to do this, no?
|
42

Not sure why no one has mentioned two Base R functions specifically to do this: str2lang() and str2expression(). These are variants of parse(), but seem to return the expression more cleanly:

eval(str2lang("5+5"))

# > 10
  
eval(str2expression("5+5"))

# > 10

Also want to push back against the posters saying that anyone trying to do this is wrong. I'm reading in R expressions stored as text in a file and trying to evaluate them. These functions are perfect for this use case.

1 Comment

It's not that it's always wrong, it's just that there are many, many cases where it's safer and better to do things in a different way.
21

Alternatively, you can use evals from my pander package to capture output and all warnings, errors and other messages along with the raw results:

> pander::evals("5+5")
[[1]]
$src
[1] "5 + 5"

$result
[1] 10

$output
[1] "[1] 10"

$type
[1] "numeric"

$msg
$msg$messages
NULL

$msg$warnings
NULL

$msg$errors
NULL


$stdout
NULL

attr(,"class")
[1] "evals"

2 Comments

Nice function; fills a hole left by evaluate::evaluate by actually returning the result object; that leaves your function suitable for use for calling via mclapply. I hope that feature remains!
Thank you, @rpierce. This function was originally written in 2011 as part of our rapport package, and have been actively maintained since then as being heavily used in our rapporter.net service besides a few other projects as well -- so I'm sure it will remain maintained for a while :) I'm glad you find it useful, thanks for your kind feedback.
21

Nowadays you can also use lazy_eval function from lazyeval package.

> lazyeval::lazy_eval("5+5")
[1] 10

Comments

11

Similarly using rlang:

eval(parse_expr("5+5"))

3 Comments

Came here looking for an rlang answer but what if any is the advantage of this over base alternatives? Actually, close examination of the code used shows that it is in fact using eval(parse(....)) which I wanted to avoid.
Not only those negatives, but its name is also misleading. It is NOT evaluating an expression. Should be called parse_to_expr ot something else to indicate that the user will know that it intended for character arguments.
It's just a wrapper that handles seamlessly any type of input (character, list, connection)
2

I agree there are concerns around eval and parse, but if the expression is in a string, there appears nothing much that can be done. This eval parse is also used in the glue package by the tidyverse experts, see https://github.com/tidyverse/glue/blob/d47d6c7701f738f8647b951df418cfd5e6a7bf76/R/transformer.R#L1-L12

Perhaps the accepted answer of eval(parse(text="5+5")) has security concerns if the text string is from an untrusted source, eg imagine user_input = "list.files()" or worse file.remove...

One potential work around is below.

The idea is to set the R environment in which the expression is to be evaluated. In R, most functions that 'comes with R' are actually in packages that gets autoloaded at R start up, eg 'list.files', 'library' and 'attach' functions come from the 'base' package. By setting the evaluation environment to empty environment, these functions are no longer available to the expression to be evaluated, preventing malicious code from executing. In the code below, by default I include only arithmetic related functions, otherwise user can provide the evaluation environment with explicitly allowed functions.

eval_text_expression <- function(text_expression, data_list, eval_envir = NULL) {
  # argument checks
  stopifnot(is.character(text_expression) && length(text_expression) == 1)
  stopifnot(is.list(data_list))
  stopifnot(length(data_list) == 0 || (!is.null(names(data_list)) && all(names(data_list) != "")))
  stopifnot(all(!(lapply(data_list, typeof) %in% c('closure', 'builtin'))))
  stopifnot(is.null(eval_envir) || is.environment(eval_envir))
  # default environment for convenience 
  if (is.null(eval_envir)) {
    arithmetic_funcs <- list("+" = `+`, "-" = `-`, "*" = `*`, "/" = `/`, "^" = `^`, "(" = `(`)
    eval_envir = rlang::new_environment(data = arithmetic_funcs, parent = rlang::empty_env())
  }
  # load data objects into evaluation environment, then evaluate expression
  eval_envir <- list2env(data_list, envir = eval_envir)
  eval(parse(text = text_expression, keep.source = FALSE), eval_envir)
}

eval_text_expression("(a+b)^c - d", list(a = 1, b = 2, c = 3, d = 4))
# [1] 23
eval_text_expression("list.files()", list())
# Error in list.files() : could not find function "list.files"
eval_text_expression("list.files()", list(), eval_envir = rlang::new_environment(list("list.files" = list.files)))
# succeeds in listing my files if i explicitly allow it

2 Comments

That looks great, but I have a little trouble understanding how it works. Could you please explain it a bit how the solution addresses the security concerns? That would make the answer more meaningful. Thanks.
edited with explanations

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.