2012-10-11 1 views
8

Можно создать дубликат:
How to write an R function that evaluates an expression within a data-frameПочему в этой функции R нет ленивой оценки?

Я хочу, чтобы написать функцию, которая сортирует data.frame - вместо того, чтобы использовать громоздкий порядок(). Принимая во внимание то, как

> 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 

Я хочу сказать, что-то вроде:

sort.df(x,b) 

Так вот моя функция:

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

Я был очень горд этим. Учитывая ленивую оценку R, я решил, что параметр ... будет оцениваться только тогда, когда это необходимо, и к тому времени он будет в области видимости, из-за «с».

Если я запускаю линию «с» напрямую, она работает. Но функция не работает.

> 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 

Что не так и как исправить? Например, я часто вижу такие «магии» в таких пакетах, как plyr. Какая уловка?

+0

sort.df (х, х $ б) работает, но до сих пор я понятия не имею, почему сортировки. df (x, b) не работает – Ali

+0

См. также 'plyr :: arr', который выполняет именно это. – hadley

+1

Спасибо! Я не знал об аранжировании, несмотря на то, что каждый день использовал плыр. Еще один пример того, что трудно найти правильные решения в мире R - и так много хорошего программирования R - это изучение лучших практик с использованием нескольких хороших пакетов. –

ответ

7

Это потому, что, когда вы передаете б вы на самом деле не передавая объект. Поместите browser внутри вашей функции, и вы увидите, что я имею в виду. Я украл это у какого-нибудь интернет-робота где-то:

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) 

будет работать.

Так будет, если вы ищете хороший способ сделать это в прикладном смысле:

library(taRifx) 
sort(x, f=~b) 
+2

+ 1 для приятного решения и, особенно, для подсказки игры с вызовом 'browser()' внутри функции. ИМХО, это далеко и далеко лучший способ узнать о «...» и всей странности, которая его окружает. –

+0

Кто-то может исправить меня по этому поводу, но 'enclos = parent.frame()' по умолчанию используется в 'eval', поэтому просто' eval (substitute (order (...)), envir = df) 'также работает :) – user1665355

9

Это будет делать то, что вы хотите:

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) 

И так это будет:

sort.df2 <- function(df, ...) { 
    cl <- substitute(list(...)) 
    cl[[1]] <- as.symbol("order") 
    df[eval(cl, envir=df),] 
} 
sort.df2(x, b, c) 
+1

Или 'sort.df <- function (df, ...) df [order (eval (substitute (...), df)),]' –

+0

@JoshuaUlrich - Не совсем то же самое. Ваша только закончит сортировку по первому элементу '...', так как 'substitute (...)' только фиксирует это. (Поместите вызов 'browser()' в 'sort.df()', а затем сравните 'substitute (...)' и 'substitute (list (...))', чтобы увидеть, что я имею в виду.) –