Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R: passing expression to an inner function

Further delving into the mysteries of R evaluation...This is closely related to my previous question ( How to write an R function that evaluates an expression within a data-frame ). Let's say I want to write a function topfn that takes a data-frame and an expression involving column-names of that data-frame. I want to pass both these arguments on to another function fn that actually evaluates the expression within the "environment" of the data-frame. And I want both fn and topfn to work correctly when passed a data-frame and an expression

My first attempt, as suggested in the answer to the above question, is to define:

 fn <- function(dfr, expr) {
   mf <- match.call()
   eval( mf$expr, envir = dfr )
 }

And define topfn like this:

topfn <- function(df, ex) {
  mf <- match.call()
  fn(df, mf$ex) 
}

Now if I have a data-frame

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

the inner function fn works fine:

> fn(df,a)
[1] 1 2 3 4 5

But the topfn does not work:

> topfn(df,a)
mf$ex

To fix this I first check the class of topfn(df,a),

> class(topfn(df,a))
[1] "call"

This gives me an idea for an ugly hack to re-define fn as follows:

fn <- function(dfr, expr) {
  mf <- match.call()
  res <- eval(mf$expr, envir = dfr)  
  if(class(res) == 'call')
    eval(expr, envir = dfr) else
  res
}

And now both functions work:

> fn(df,a)
[1] 1 2 3 4 5
> topfn(df,a)
[1] 1 2 3 4 5

As I said, this looks like an ugly hack. Is there a better way (or more standard idiom) to get these working? I've consulted Lumley's curiously-named Standard NonStandard Evaluation Rules document http://developer.r-project.org/nonstandard-eval.pdf but wasn't particularly enlightened after reading it. Also helpful would be any pointers to source-code of functions I can look at for examples.

like image 357
Prasad Chalasani Avatar asked Jan 14 '11 14:01

Prasad Chalasani


People also ask

Can we pass an expression to a function?

Short answer: You can't. The result of the expression evaluation is passed to the function rather than the expression itself.

Can you write a function inside a function in R?

A nested function or the enclosing function is a function that is defined within another function. In simpler words, a nested function is a function in another function. There are two ways to create a nested function in the R programming language: Calling a function within another function we created.

Can you pass functions as parameters in R?

In R, you can pass a function as an argument. You can also pass function code to an argument. Then, you can assign the complete code of a function to a new object.


2 Answers

This is most easily avoided by passing strings into topfn instead of expressions.

topfn <- function(df, ex_txt) 
{
  fn(df, ex_txt) 
}

fn <- function(dfr, expr_txt) 
{        
   eval(parse(text = expr_txt), dfr) 
}

df <- data.frame(a = 1:5, b = 1:5 )
fn(df, "a")                              
fn(df, "2 * a + b")
topfn(df, "a")             
topfn(df, "2 * a + b")

EDIT:

You could let the user pass expressions in, but use strings underneath for your convenience.

Change topfn to

topfn <- function(df, ex) 
{
  ex_txt <- deparse(substitute(ex))
  fn(df, ex_txt) 
}
topfn(df, a)             
topfn(df, 2 * a + b)

ANOTHER EDIT:

This seems to work:

topfn <- function(df, ex) 
{
  eval(substitute(fn(df, ex)))
}

fn <- function(dfr, expr) 
{        
   eval(substitute(expr), dfr) 
}
fn(df, a)                              
fn(df, 2 * a + b)
topfn(df, a)             
topfn(df, 2 * a + b)
like image 152
Richie Cotton Avatar answered Sep 30 '22 16:09

Richie Cotton


You can use three dots to gather arguments and pass them to another function, is that what you mean?

ftop=function(...) f(...)
f=function(a,b) a[b]

a=data.frame(b=10)

ftop(a,"b")

f(a,"b")
like image 26
Sacha Epskamp Avatar answered Sep 30 '22 16:09

Sacha Epskamp