2012-10-11 15 views
8

Eventuali duplicati:
How to write an R function that evaluates an expression within a data-framePerché la valutazione lazy non funziona in questa funzione R?

Voglio scrivere una funzione che ordina un data.frame - invece di usare l'ordine ingombrante(). Dato qualcosa come

> 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 

voglio dire qualcosa come:

sort.df(x,b) 

Quindi, ecco la mia funzione:

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

ero davvero orgoglioso di questo. Data la pigra valutazione di R, ho pensato che il parametro ... sarebbe stato valutato solo quando necessario - e a quel punto sarebbe stato in ambito, a causa di 'con'.

Se eseguo direttamente la riga 'with', funziona. Ma la funzione 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 

Cosa c'è che non va e come risolverlo? Per esempio, vedo spesso questo tipo di "magia" in pacchetti come plyr. Qual è il trucco?

+0

sort.df (x, x $ b) funziona, ma ancora non ho idea perché sorta. df (x, b) non funziona – Ali

+0

Vedi anche 'plyr :: arrange' che fa esattamente questo. – hadley

+1

Grazie! Non sapevo di organizzare nonostante l'uso di Plyr ogni giorno. Un altro esempio è che è difficile trovare le giuste soluzioni nel mondo R - e così tanta buona programmazione R sta imparando le migliori pratiche usando pochi buoni pacchetti. –

risposta

7

E 'perché quando si sta passando b si sta effettivamente non passaggio di un oggetto. Metti un browser all'interno della tua funzione e vedrai cosa intendo. L'ho rubato da qualche robot Internet da qualche 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) 

funzionerà.

così sarà, se siete alla ricerca di un bel modo per fare questo in senso applicata:

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

+ 1 per la bella soluzione e, soprattutto, per suggerire di giocare con una chiamata 'browser()' all'interno della funzione. IMHO, questo è di gran lunga il modo migliore per imparare "..." e tutta la stranezza che lo circonda. –

+0

Qualcuno potrebbe correggermi su questo, ma 'enclos = parent.frame()' è predefinito in 'eval' quindi semplicemente' eval (sostituto (ordine (...)), envir = df) 'funziona anche :) – user1665355

9

Questo farà quello che vuoi:

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) 

E così sarà questo:

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

Oppure 'sort.df <- function (df, ...) df [ordine (eval (sostituto (...), df)),' –

+0

@JoshuaUlrich - Non proprio la stessa cosa. Il vostro sarà finito solo per ordinare dal primo elemento di '...', dal momento che 'sostituto (...)' lo cattura. (Metti una chiamata 'browser()' in 'sort.df()', quindi confronta 'sostituto (...) 'e' sostituto (lista (...)) 'per vedere cosa intendo.) –

Problemi correlati