Apply a list of n *expressions* to each row of a dataframe?

756 views Asked by At

In short, I have a list of expressions that I want to apply to each row of a dataframe. This is very similar to this question, but there is a subtle difference in that I do not have a list of functions, but have a list of expressions. Here's what I am attempting:

I have a dataframe of equation terms that I pass to paste to form text strings of expressions:

# build a test dataframe
a <- c(10, 11, 12)
b <- c(0.5, 06, 0.7)
c <- c(300, 400, 500)
d <- c(1000, 2000, NA)
p <- c(1.32, 1.37, 1.43)
m <- c(0.45, .55, .65)
params <- data.frame(a, b, c, d, p, m)

# create vector of expressions
ff.txt <- paste(params$a, "*2 + ",
    params$b, "*3 + ",
    params$c, "*4 + ",
    params$d, "*5 * ",
    "x * ", 
    params$p, " * ",
    params$m, 
    sep = "")

This will return a vector of expressions stored as text, such as "10*2 + 0.5*3 + 300*4 + 1000*5 * x * 1.32 * 0.45".

I then wrote a function to convert the text expression to an R expression using parse:

funs.list <- lapply(funs.txt, function(x) parse(file = "", text = x))

... where funs.txt is the vector of expressions stored as text.

Now, this is where I'm having difficulty. Imagine funs.list contains two expressions. If I do something like:

lapply(funs.list, eval, envir = list(x=c(7, 60)))

... I get back a 2x2 matrix, where I only want the answers are on the diagonal. (Ultimately I'm going to have about 1,000 expressions that will be called repeatedly as part of an optimization, so do not want the additional computational effort.)

The other approach I've tried is:

mapply(do.call, funs.list, lapply(params, list))

... but I get an error message from do.call stating 'what' must be a character string or a function.

Any suggestions on where to go from here?

2

There are 2 answers

0
Nick Kennedy On BEST ANSWER

If I understand correctly you want to evaluate the first expression with the first value of x, the second with the second etc.

You could do:

mapply(function(ex, x) eval(ex, envir = list(x = x)), funs.list[1:2], c(7, 60))
0
Tim Biegeleisen On

I feel that it would be a better design decision to define a custom R function corresponding to your algebraic expression and make use of the apply() function in row mode (which you appear to have overlooked in your original problem). This function also takes x as an input, and so I also cbind the x vector to the original data frame.

exp <- function(a, b, c, d, p, m, x) {
           result <- 2*a + 3*b + 4*c + 5*d*x*p*m
           return(result)
       }

x <- c(1, 2, 3)
params <- cbind(params, x)

output <- apply(params, 1, function(x) {
                               exp(x['a']], x['b'], x['c'], x[["d"]],
                                   x['p'], x['m'], x['x'])
                           })