2011-01-14 3 views
13

Weitere Einblicke in die Geheimnisse der R-Bewertung ... Dies ist eng verwandt mit meiner vorherigen Frage (How to write an R function that evaluates an expression within a data-frame). Nehmen wir an, ich möchte eine Funktion topfn schreiben, die einen Datenrahmen und einen Ausdruck mit Spaltennamen dieses Datenrahmens annimmt. Ich möchte diese beiden Argumente an eine andere Funktion übergeben fn, die tatsächlich den Ausdruck innerhalb der "Umgebung" des Datenrahmens auswertet. Und ich will beide fn und topfn korrekt arbeiten, wenn ein Datenrahmen und einen Ausdruck übergebenR: Ausdruck an eine innere Funktion übergeben

Mein erster Versuch, wie in der Antwort auf die obige Frage vorgeschlagen, ist zu definieren:

fn <- function(dfr, expr) { 
    mf <- match.call() 
    eval(mf$expr, envir = dfr) 
} 

und definieren topfn wie folgt aus:

topfn <- function(df, ex) { 
    mf <- match.call() 
    fn(df, mf$ex) 
} 

wenn ich nun ein Datenrahmen

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

die innere Funktion fn gut funktioniert:

> fn(df,a) 
[1] 1 2 3 4 5 

Aber die topfn funktioniert nicht:

> topfn(df,a) 
mf$ex 

dies zu beheben ich zuerst die Klasse von topfn(df,a) überprüfen,

> class(topfn(df,a)) 
[1] "call" 

Das gibt mir eine Idee für einen hässlichen Hack, umneu zu definierenwie folgt:

fn <- function(dfr, expr) { 
    mf <- match.call() 
    res <- eval(mf$expr, envir = dfr) 
    if(class(res) == 'call') 
    eval(expr, envir = dfr) else 
    res 
} 

Und nun beide Funktionen arbeiten:

> fn(df,a) 
[1] 1 2 3 4 5 
> topfn(df,a) 
[1] 1 2 3 4 5 

Wie gesagt, das ist wie eine hässliche Hack aussieht. Gibt es einen besseren Weg (oder mehr Standard-Idiom), um diese funktionieren zu lassen? Ich habe Lumleys seltsamerweise benannte Standard NonStandard Evaluation Rules Dokument http://developer.r-project.org/nonstandard-eval.pdf konsultiert, aber war nicht besonders erleuchtet nach dem Lesen. Hilfreich wären auch Hinweise auf Quellcodes von Funktionen, die ich für Beispiele betrachten kann.

+0

Es gibt ein großes Wiki zum Thema R Auswertung von Hadley Wickham hier: https: //github.com/hadley/devtools/wiki/Evaluation –

+1

Und wie ich Ihnen vorschlage, möchten Sie sorgfältig zwischen Funktionen unterscheiden, die interaktiv verwendet werden, und das funktioniert werden von anderen Funktionen aufgerufen. Es ist sehr schwierig, eine Funktion aufzurufen, die Ersatz- und andere Tricks verwendet. Mit anderen Worten, es gibt keinen sauberen Weg für "fn" und "topfn", um mit den gleichen Arten von Eingaben zu arbeiten - und das ist eine GUTE Sache. – hadley

+1

@hadley Ich sehe Ihren Punkt: Funktionen für die interaktive Verwendung können Ersatz/andere Tricks verwenden, um es einfach zu machen, sie an der Konsole (d. H. Keine Anführungszeichen, usw.) einzugeben. Und Funktionen, die nur dazu bestimmt sind, von anderen Funktionen aufgerufen zu werden, sollten solche Tricks nicht verwenden, da sie unvorhersehbar brechen können. Insbesondere die Lösung von @Richie am Ende seiner Antwort, obwohl es in meinem speziellen Szenario oben funktioniert - es kann brechen (vor allem "fn"), wenn in einem anderen Szenario verwendet - ich denke, das ist, was Sie " sag ich, oder? –

Antwort

14

Dies wird am einfachsten vermieden, indem Strings anstelle von Ausdrücken in topfn übergeben werden.

topfn <- function(df, ex_txt) 
{ 
    fn(df, ex_txt) 
} 

fn <- function(dfr, expr_txt) 
{   
    eval(parse(text = expr_txt), dfr) 
} 

df <- data.frame(a = 1:5, b = 1:5) 
fn(df, "a")        
fn(df, "2 * a + b") 
topfn(df, "a")    
topfn(df, "2 * a + b") 

EDIT:

Sie könnten die Benutzer Pass Ausdrücke, lassen aber Strings unter für Ihre Bequemlichkeit verwenden.

ändern topfn zu

topfn <- function(df, ex) 
{ 
    ex_txt <- deparse(substitute(ex)) 
    fn(df, ex_txt) 
} 
topfn(df, a)    
topfn(df, 2 * a + b) 

ANOTHER EDIT:

Dies scheint zu funktionieren:

topfn <- function(df, ex) 
{ 
    eval(substitute(fn(df, ex))) 
} 

fn <- function(dfr, expr) 
{   
    eval(substitute(expr), dfr) 
} 
fn(df, a)        
fn(df, 2 * a + b) 
topfn(df, a)    
topfn(df, 2 * a + b) 
+0

@richie: Danke, ja, aber ich suchte nach einer Möglichkeit, Strings zu vermeiden. Ich möchte, dass die Funktionen genau wie viele andere Funktionen in R funktionieren, die nicht zitierte Ausdrücke als Argumente verwenden. –

+0

Siehe meine Antwort auf die vorherige Frage für eine dritte (Gitter-Stil) Spezifikation von "Fn". http://stackoverflow.com/questions/4682709/how-to-write-an-r-function-that-evaluates-anexpression-within-a-data-frame –

+0

@Richie Ja, ich sah das, danke, es ist eigentlich der einfachste Weg, um es für die innere Funktion zu arbeiten, aber die Anpassung an den oben geschachtelten Fall ist gleichermaßen problematisch. –

1

Sie können drei Punkte verwenden, um Argumente zu sammeln und sie an eine andere Funktion zu übergeben. Ist das das, was Sie meinen?

+0

nicht ganz. Ich möchte keine Punkte verwenden, ich möchte, dass die expliziten Argumente an die innere Funktion weitergegeben werden. –