2012-10-11 11 views
8

Duplicar posible:
How to write an R function that evaluates an expression within a data-frame¿Por qué no funciona la evaluación perezosa en esta función R?

Quiero escribir una función que ordena una hoja.de.datos - en lugar de utilizar el orden engorroso(). Teniendo en cuenta algo así como

> 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 

quiero decir algo como:

sort.df(x,b) 

Así que aquí está mi función:

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

Yo estaba muy orgulloso de esto. Dada la evaluación perezosa de R, calculé que el parámetro ... solo se evaluaría cuando fuera necesario, y para ese momento estaría dentro del alcance, debido a 'con'.

Si ejecuto la línea 'con' directamente, funciona. Pero la función no.

> 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 

¿Qué problema hay y cómo solucionarlo? Veo este tipo de "magia" frecuentemente en paquetes como plyr, por ejemplo. ¿Cuál es el truco?

+0

sort.df (x, x $ b) funciona, pero todavía no tengo idea de qué tipo. df (x, b) no funciona – Ali

+0

Ver también 'plyr :: arrange' que hace exactamente esto. – hadley

+1

¡Gracias! No sabía acerca de organizar a pesar de usar plyr todos los días. Otro ejemplo más de que es difícil encontrar las soluciones correctas en el mundo R, y gran parte de la buena programación R es aprender las mejores prácticas con unos pocos paquetes buenos. –

Respuesta

7

Es porque cuando estás pasando b realidad estás no pasar un objeto. Ponga un browser dentro de su función y verá lo que quiero decir. Lo robé de algún robot de Internet en alguna parte:

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) 

funcionará.

también lo hará si usted está buscando una buena manera de hacer esto en un sentido aplicada:

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

+ 1 para la buena solución y, especialmente, para sugerir jugar con una llamada 'browser()' dentro de la función. En mi humilde opinión, esa es de lejos la mejor manera de aprender sobre '...' y toda la rareza que lo rodea. –

+0

Alguien podría corregirme en esto, pero 'enclos = parent.frame()' está predeterminado en 'eval' así que simplemente' eval (substitute (order (...)), envir = df) 'también funciona :) – user1665355

9

Esto va a hacer lo que quiere:

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) 

y también lo hará la siguiente:

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

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

+0

@JoshuaUlrich - No es lo mismo. El tuyo solo terminará ordenando por el primer elemento de '...', ya que 'substitute (...)' solo captura eso. (Ponga una llamada 'browser()' en 'sort.df()', y luego compare 'substitute (...)' y 'substitute (list (...))' para ver a qué me refiero.) –

Cuestiones relacionadas