2011-01-13 4 views
9

Puzzle per intenditori R: Supponiamo di avere un data-frame:Come scrivere una funzione R che valuta l'espressione all'interno di un data-frame

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

So che possiamo fare le cose come

with(df, a) 

per ottenere un vettore di risultati.

Ma come faccio a scrivere una funzione che prende un'espressione (come a o a > 3) e fa la stessa cosa dentro. Cioè Voglio scrivere una funzione fn che accetta un quadro dati e un'espressione come argomenti e restituisce il risultato della valutazione dell'espressione "all'interno" del frame dei dati come ambiente.

Non importa se questo sembra forzato (potrei semplicemente usare with come sopra), ma questa è solo una versione semplificata di una funzione più complessa che sto scrivendo. Ho provato diverse varianti (usando eval, with, envir, substitute, local, ecc.) Ma nessuna di queste funzioni. Per esempio, se io definisco fn in questo modo:

fn <- function(dat, expr) { 
    eval(expr, envir = dat) 
} 

ottengo questo errore:

> fn(df, a) 
Error in eval(expr, envir = dat) : object 'a' not found 

Chiaramente mi manca qualcosa di sottile sugli ambienti e valutazione. C'è un modo per definire una funzione del genere?

+1

C'è una sezione su [wiki di Hadley su questo argomento] (https: // github.com/hadley/devtools/wiki/Evaluation) – Marek

+0

@Marek è un ottimo riferimento da leggere, grazie! –

+0

Questa pagina è ancora accessibile? –

risposta

10

Il pacchetto reticolo fa questo genere di cose in un modo diverso. Vedi, ad esempio, lattice:::xyplot.formula.

fn <- function(dat, expr) { 
    eval(substitute(expr), dat) 
} 
fn(df, a)    # 1 2 3 4 5 
fn(df, 2 * a + b)  # 3 6 9 12 15 
+0

+1 questo è il modo più semplice, grazie –

+0

+1, molto bello (non pensavo a un sostituto). Il vantaggio di match.call è che hai tutti i tuoi argomenti in un elenco conveniente, ed è per questo che lo uso più spesso. Ma se non hai bisogno del resto, il sostituto è davvero un modo molto carino e facile. –

+0

Esiste un modo per passare più espressioni in un elenco() o c() e valutare ciascuno in un ciclo for per diversi frame di dati che sono anche memorizzati in un elenco? Voglio la stessa funzionalità Non riesco proprio a farlo funzionare per i dataframes e le espressioni memorizzate nell'elenco. – Blind0ne

9

Questo perché non stai passando un'espressione.

Prova:

fn <- function(dat, expr) { 
    mf <- match.call() # makes expr an expression that can be evaluated 
eval(mf$expr, envir = dat) 
} 

> df <- data.frame(a = 1:5, b = 1:5) 
> fn(df, a) 
[1] 1 2 3 4 5 
> fn(df, a+b) 
[1] 2 4 6 8 10 

Una rapida occhiata al codice sorgente delle funzioni utilizzare questo (ad esempio lm) può rivelare molto cose più interessanti su di esso.

+0

grazie, questo è quello che mi manca! E sì, ho provato a guardare le funzioni come 'subset', e alcune altre, per vedere come lo fanno, ma erano internals. Non ho pensato a 'lm', buon punto per riferimento futuro. –

+1

Penso che usare il sostituto in questa circostanza sia più canonico. E non sono sicuro che sia un buon modello - almeno assicurati di leggere le regole di valutazione standard non standard. – hadley

+0

@hadley: vero. Ho solo pensato a 'match.call()' e 'lm()' a causa dell'argomento 'data'. –

-1

? All'interno potrebbe anche essere di interesse.

df <- data.frame(a = 1:5, b = 1:5) 
within(df, cx <- a > 3) 
    a b cx 
1 1 1 FALSE 
2 2 2 FALSE 
3 3 3 FALSE 
4 4 4 TRUE 
5 5 5 TRUE 
+0

@mdsummer: temo che tu non abbia colto completamente la domanda ... –

2

Una voce in ritardo, ma l'approccio e la sintassi data.table sembrerebbe essere quello che sono dopo. Questo è esattamente il modo in cui [.data.table funziona con gli argomenti j, i e by.

Se ne avete bisogno in forma fn(x,expr), quindi è possibile utilizzare il seguente

library(data.table) 

DT <- data.table(a = 1:5, b = 2:6) 

`[`(x=DT, j=a) 

## [1] 1 2 3 4 5 

`[`(x=DT, j=a * b) 
## [1] 2 6 12 20 30 

penso che sia più facile da usare in forma più nativa

DT[,a] 
## [1] 1 2 3 4 5 

e così via. Sullo sfondo si utilizza substitute e eval

Problemi correlati