Search code examples
rexpressionnon-standard-evaluation

How to save an expression with multiplication to later call it?


I'm looking to save an expression like this:

expression <- c(beta_a*a + beta_b*b + beta_d*d) 

And I want to return this expression:

expression
> beta_a*a + beta_b*b + beta_d*d

But when I try to run the first line, it just says the error "Object 'beta_a' not found".

Ultimately, I'm using this for Apollo for an ordered logit model, where I have to make 60 different utility specifications. I thought it would be easier bundle the specifications into a short expression, so I can rerun the same code only changing the specification. A specification both consists of defining the beta variables and the utility functions. I was able to use this method for the beta values that have a structure like this:

betas = c(beta_a = 0, beta_b = 0, beta_d = 0)

To turn it into this:

b_specification1 <- c(beta_a = 0, beta_b = 0, beta_d = 0)
betas = b_specification1

But doing the utility function:

u_specification1 <- c(beta_a*a + beta_b*b + beta_d*d) 
utility = u_specification1

Gives the error "object 'beta_a' not found". Both b_specification1 and u_specification1 are run before betas and utility.


Solution

  • Define the expression using quote and then use eval to evaluate it.

    e <- quote(a + b)
    a <- 1
    b <- 2
    eval(e)
    ## [1] 3
    

    It is also possible to define it as a character string in which case parse it and then evaluate it.

    ch <- "a + b"
    a <- 1
    b <- 2
    eval(parse(text = ch))
    ## [1] 3
    

    Note that eval has an envir argument in case you need to evaluate it looking up variable names in a different environment than the current one. For example,

    f <- function(s, envir = parent.frame()) {
      eval(parse(text = s), envir)
    }
    
    a <- 1
    b <- 2
    g <- function() {
      a <- 3
      b <- 4
      c(f("a+b"), f("a+b", .GlobalEnv))
    }
    g()
    # [1] 7 3
    

    To combine multiple expressions use substitute but be careful because it acts differently when run at top level at the console vs within a function. The second argument in the first example is not needed if run within a funciton.

    spec1 <- quote(beta_a * a + beta_b * b)
    spec2 <- quote(beta_d * d + beta_e * e)
    
    # run at top level
    substitute(spec1 + spec2, as.list(environment()))
    ## beta_a * a + beta_b * b + (beta_d * d + beta_e * e)
    
    # same
    substitute(spec1 + spec2, list(spec1 = spec1, spec2 = spec2))
    ## beta_a * a + beta_b * b + (beta_d * d + beta_e * e)
    
    
    # run within a function
    f <- function() {
      spec1 <- quote(beta_a * a + beta_b * b)
      spec2 <- quote(beta_d * d + beta_e * e)
      substitute(spec1 + spec2)
    }
    
    if (exists("spec1")) rm(spec1)
    if (exists("spec2")) rm(spec2)
    
    f()
    ## beta_a * a + beta_b * b + (beta_d * d + beta_e * e)
    

    If using character string use paste or sprintf.