Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write an R function that evaluates an expression within a data-frame

Puzzle for the R cognoscenti: Say we have a data-frame:

df <- data.frame( a = 1:5, b = 1:5 )

I know we can do things like

with(df, a)

to get a vector of results.

But how do I write a function that takes an expression (such as a or a > 3) and does the same thing inside. I.e. I want to write a function fn that takes a data-frame and an expression as arguments and returns the result of evaluating the expression "within" the data-frame as an environment.

Never mind that this sounds contrived (I could just use with as above), but this is just a simplified version of a more complex function I am writing. I tried several variants ( using eval, with, envir, substitute, local, etc) but none of them work. For example if I define fn like so:

fn <- function(dat, expr) {
  eval(expr, envir = dat)
}

I get this error:

> fn( df, a )
Error in eval(expr, envir = dat) : object 'a' not found

Clearly I am missing something subtle about environments and evaluation. Is there a way to define such a function?

like image 695
Prasad Chalasani Avatar asked Jan 13 '11 16:01

Prasad Chalasani


2 Answers

The lattice package does this sort of thing in a different way. See, e.g., lattice:::xyplot.formula.

fn <- function(dat, expr) {
  eval(substitute(expr), dat)
}
fn(df, a)             # 1 2 3 4 5
fn(df, 2 * a + b)     # 3 6 9 12 15
like image 195
Richie Cotton Avatar answered Nov 02 '22 23:11

Richie Cotton


That's because you're not passing an expression.

Try:

fn <- function(dat, expr) {
  mf <- match.call() # makes expr an expression that can be evaluated
 eval(mf$expr, envir = dat)
}

> df <- data.frame( a = 1:5, b = 1:5 )
> fn( df, a )
[1] 1 2 3 4 5
> fn( df, a+b )
[1]  2  4  6  8 10

A quick glance at the source code of functions using this (eg lm) can reveal a lot more interesting things about it.

like image 36
Joris Meys Avatar answered Nov 02 '22 21:11

Joris Meys