Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't lazy evaluation work in this R function? [duplicate]

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

I want to write a function that sorts a data.frame -- instead of using the cumbersome order(). Given something like

> x=data.frame(a=c(5,6,7),b=c(3,5,1))
> x
  a b
1 5 3
2 6 5
3 7 1

I want to say something like:

sort.df(x,b)

So here's my function:

sort.df <- function(df, ...) {
  with(df, df[order(...),])
}

I was really proud of this. Given R's lazy evaluation, I figured that the ... parameter would only be evaluated when needed -- and by that time it would be in scope, due to 'with'.

If I run the 'with' line directly, it works. But the function doesn't.

> with(x,x[order(b),])
  a b
3 7 1
1 5 3
2 6 5
> sort.df(x,b)
Error in order(...) : object 'b' not found

What's wrong and how to fix it? I see this sort of "magic" frequently in packages like plyr, for example. What's the trick?

like image 338
dk. Avatar asked Oct 11 '12 17:10

dk.


People also ask

How lazy evaluation works in R?

Lazy evaluation is implemented in R as it allows a program to be more efficient when used interactively: only the necessary symbols are evaluated, that is to say that only the needed objects will be loaded in memory and/or looked for.

What is a lazy function evaluation in R give some example?

Lazy evaluation is an evaluation strategy which holds the evaluation of an expression until its value is needed. It avoids repeated evaluation. Haskell is a good example of such a functional programming language whose fundamentals are based on Lazy Evaluation.


2 Answers

This will do what you want:

sort.df <- function(df, ...) {
  dots <- as.list(substitute(list(...)))[-1]
  ord <- with(df, do.call(order, dots))
  df[ord,]
}

## Try it out
x <- data.frame(a=1:10, b=rep(1:2, length=10), c=rep(1:3, length=10))
sort.df(x, b, c)

And so will this:

sort.df2 <- function(df, ...) {
    cl <- substitute(list(...))
    cl[[1]] <- as.symbol("order")
    df[eval(cl, envir=df),]
}
 sort.df2(x, b, c)
like image 135
Josh O'Brien Avatar answered Oct 21 '22 21:10

Josh O'Brien


It's because when you're passing b you're actually not passing an object. Put a browser inside your function and you'll see what I mean. I stole this from some Internet robot somewhere:

x=data.frame(a=c(5,6,7),b=c(3,5,1))

sort.df <- function(df, ..., drop = TRUE){
    ord <- eval(substitute(order(...)), envir = df, enclos = parent.frame())
    return(df[ord, , drop = drop])
}

sort.df(x, b)

will work.

So will if you're looking for a nice way to do this in an applied sense:

library(taRifx)
sort(x, f=~b)
like image 7
Tyler Rinker Avatar answered Oct 21 '22 19:10

Tyler Rinker