2011-01-13 5 views
9

Puzzle für die R cognoscenti auswertet: Sagen wir einen Datenrahmen haben:Wie eine R-Funktion zu schreiben, die einen Ausdruck in einem Datenrahmen

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

Ich weiß, dass wir Dinge wie

with(df, a) 
tun können

um einen Vektor der Ergebnisse zu erhalten.

Aber wie schreibe ich eine Funktion, die einen Ausdruck (wie a oder a > 3) nimmt und das Gleiche tut innen. I.e. Ich möchte eine Funktion fn schreiben, die einen Datenrahmen und einen Ausdruck als Argumente nimmt und das Ergebnis der Auswertung des Ausdrucks "innerhalb" des Datenrahmens als eine Umgebung zurückgibt.

Es macht nichts, dass dies künstlich klingt (ich könnte einfach with wie oben verwenden), aber das ist nur eine vereinfachte Version einer komplexeren Funktion, die ich schreibe. Ich versuchte mehrere Varianten (unter Verwendung eval, with, envir, substitute, local, usw.), aber keiner von ihnen funktioniert. Zum Beispiel, wenn ich definieren fn wie so:

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

ich diesen Fehler:

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

Klar bin ich etwas fehlt subtil über Umgebungen und Auswertung. Gibt es eine Möglichkeit, eine solche Funktion zu definieren?

+1

Es gibt Abschnitt auf [Hadleys Wiki zu diesem Thema] (https: // github.com/hadley/devtools/wiki/Evaluation) – Marek

+0

@Marek das ist eine gute Referenz zu lesen, danke! –

+0

Ist diese Seite immer noch zugänglich? –

Antwort

10

Das Gitterpaket macht diese Art von Sache auf eine andere Art und Weise. Siehe beispielsweise 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 das ist der einfachste Weg, danke –

+0

+1, sehr nett (habe nicht über Ersatz nachgedacht). Der Vorteil von match.call ist, dass Sie alle Ihre Argumente in einer praktischen Liste haben, weshalb ich diese öfter verwende. Aber wenn Sie den Rest nicht brauchen, ist Ersatz in der Tat ein sehr netter und einfacher Weg. –

+0

Gibt es eine Möglichkeit, mehrere Ausdrücke in einer list() oder c() zu übergeben und jeweils in einer for-Schleife für verschiedene Datenrahmen auszuwerten, die ebenfalls in einer Liste gespeichert sind? Ich möchte die gleiche Funktionalität, ich kann es nicht nur für Datenrahmen und Ausdrücke in der Liste arbeiten lassen. – Blind0ne

9

Das liegt daran, dass Sie keinen Ausdruck übergeben.

Versuche:

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 

Ein kurzer Blick auf dem Quellcode Funktionen dieses (zB lm) viele interessante Dinge über sie offenbaren.

+0

danke, das ist was ich vermisse! Und ja, ich habe versucht, Funktionen wie "Teilmenge" und einige andere zu betrachten, um zu sehen, wie sie es machen, aber sie waren Interna. Ich dachte nicht an "lm", guter Punkt für zukünftige Referenz. –

+1

Ich denke, Ersatz in diesem Fall ist mehr kanonisch. Und ich bin mir nicht sicher, ob LM ein gutes Vorbild ist - stellen Sie zumindest sicher, dass Sie die Standard-Nicht-Standard-Bewertungsregeln lesen. – hadley

+0

@hadley: wahr. Ich dachte nur an 'match.call()' und 'lm()' wegen des Arguments 'data'. –

-1

? Innerhalb könnte auch von Interesse sein.

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: Ich fürchte, du hast die Frage nicht vollständig verstanden ... –

2

Eine Nachmeldung, aber die data.table Ansatz und Syntax scheint zu sein, was Sie nach. Genau so funktioniert [.data.table mit den j, i und by Argumenten.

Wenn Sie es in Form fn(x,expr) benötigen, dann können Sie mit den folgenden

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 

Ich denke, es ist einfacher zu bedienen ist in mehr nativer Form

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

und so weiter. Im Hintergrund ist dies substitute und eval

Verwandte Themen