2016-04-15 2 views
8

Nicht-Standard-Auswertung ist wirklich praktisch, wenn dplyrs Verben verwendet. Aber es kann problematisch sein, wenn diese Verben mit Funktionsargumenten verwendet werden. Zum Beispiel lassen Sie uns sagen, dass ich eine Funktion erstellen möchte, die gibt mir die Anzahl der Zeilen für eine bestimmte Spezies.Erstellen einer Funktion mit einem Argument, das an dplyr :: filter übergeben wird, was ist der beste Weg, um nse zu umgehen?

# Load packages and prepare data 
library(dplyr) 
library(lazyeval) 
# I prefer lowercase column names 
names(iris) <- tolower(names(iris)) 
# Number of rows for all species 
nrow(iris) 
# [1] 150 

Beispiel nicht funktionierend

dieser Funktion als nicht funktioniert, weil erwartet species im Rahmen des Irisdatenrahmen interpretiert statt im Rahmen der Funktionsargument interpretiert wird:

nrowspecies0 <- function(dtf, species){ 
    dtf %>% 
     filter(species == species) %>% 
     nrow() 
} 
nrowspecies0(iris, species = "versicolor") 
# [1] 150 

3 Beispiele für die Umsetzung

Um Nicht-Standard-Auswertung zu arbeiten, ich in der Regel das Argument mit einem Unterstrich anhängen:

nrowspecies1 <- function(dtf, species_){ 
    dtf %>% 
     filter(species == species_) %>% 
     nrow() 
} 

nrowspecies1(iris, species_ = "versicolor") 
# [1] 50 
# Because of function name completion the argument 
# species works too 
nrowspecies1(iris, species = "versicolor") 
# [1] 50 

Es ist nicht völlig zufrieden stellend ist , da sie den Namen des Funktionsargument zu etwas weniger benutzerfreundlich ändert. Oder es beruht auf Autovervollständigung , die ich befürchte, ist keine gute Praxis für die Programmierung. Um ein schönes Argument Namen zu behalten, ich tun konnte:

nrowspecies2 <- function(dtf, species){ 
    species_ <- species 
    dtf %>% 
     filter(species == species_) %>% 
     nrow() 
} 
nrowspecies2(iris, species = "versicolor") 
# [1] 50 

Eine andere Möglichkeit, um Nicht-Standard-Auswertung auf this answer Basis zu arbeiten. interp() interpretiert species im Rahmen der Funktion Umgebung:

nrowspecies3 <- function(dtf, species){ 
    dtf %>% 
     filter_(interp(~species == with_species, 
         with_species = species)) %>% 
     nrow() 
} 
nrowspecies3(iris, species = "versicolor") 
# [1] 50 

Betrachtet man die 3-Funktion oben, was ist der bevorzugte - robusteste - Weg, um diese Filterfunktion zu implementieren? Gibt es noch andere Möglichkeiten?

Antwort

3

Diese Frage hat absolut nichts mit einer nicht standardisierten Auswertung zu tun. Lassen Sie mich Ihre ursprüngliche Funktion umschreiben, dass klar zu machen:

nrowspecies4 <- function(dtf, boo){ 
    dtf %>% 
     filter(boo == boo) %>% 
     nrow() 
} 
nrowspecies4(iris, boo = "versicolor") 
#150 

Der Ausdruck in Ihrem filter ergibt immer TRUE (fast immer - siehe Beispiel unten), das ist, warum es nicht, nicht wegen irgendeiner NSE Magie funktioniert .

Ihre nrowspecies2 ist der Weg zu gehen.

FWIW, species in Ihrem nrowspecies0 ist in der Tat als eine Säule ausgewertet, nicht als Eingangsgröße species, und Sie können überprüfen, ob durch nrowspecies0(iris, NA)-nrowspecies4(iris, NA) vergleichen.

5

Die Antwort von @eddi ist richtig, was hier vor sich geht. Ich schreibe eine andere Antwort, die die größere Anforderung an das Schreiben von Funktionen mit dplyr Verben adressiert.Sie werden feststellen, dass es letztlich so etwas wie nrowspecies2 verwendet, um die species == species Tautologie zu vermeiden.

Um eine Funktion Verpackung dplyr Verb (n) schreiben, die mit NSE, schreiben zwei Funktionen arbeiten:

Erste eine Version schreiben, zitiert Eingaben erfordert, mit lazyeval und eine SE Version des dplyr Verb. Also in diesem Fall filter_.

nrowspecies_robust_ <- function(data, species){ 
    species_ <- lazyeval::as.lazy(species) 
    condition <- ~ species == species_ # * 
    tmp <- dplyr::filter_(data, condition) # ** 
    nrow(tmp) 
} 
nrowspecies_robust_(iris, ~versicolor) 

Zweite eine Version machen, die NSE verwendet:

nrowspecies_robust <- function(data, species) { 
    species <- lazyeval::lazy(species) 
    nrowspecies_robust_(data, species) 
} 
nrowspecies_robust(iris, versicolor) 

* = wenn Sie etwas komplizierter machen wollen, müssen Sie unter unten stehenden Link lazyeval::interp hier wie in den Spitzen verwenden

** = auch, wenn Sie Ausgangsnamen ändern müssen, finden Sie in der .dots Argument

+0

Danke, die E-Mail von Hadley Sie erwähnt mir 'Vignette aussehen (“ lazyeval ")' was erklärt, dass "Jede Funktion, die NSE verwendet, sollte eine Standardauswertungs (SE) Escape-Schraffur haben, die die eigentliche Berechnung ausführt. Der SE-Funktionsname sollte mit _ enden." Ich möchte eine Erklärung dessen haben, was Hadley am Ende der "Lazyeval" -Vignette mit "geeignet zum Programmieren" meint. Bedeutet das, dass ich nse Inside Funktionen nicht benutzen sollte? –

+0

Ja, oder zumindest sollten Sie es vermeiden, wenn möglich. Siehe auch Abschnitt "Nachteile der Nicht-Standard-Bewertung" hier http://adv-r.had.co.nz/Computing-on-the-language.html Das grundlegende Problem, wie Hadley in diesem Kapitel erklärt, ist NSE ist Es ist sehr schwierig, innerhalb eines Programms nachzudenken, weil Funktionen in verschiedenen Kontexten anders funktionieren können. Das heißt, wenn sie interaktiv verwendet werden, kann eine NSE-Funktion anders als bei einer Verwendung in einer Funktion wirken. – jaimedash

+1

Hadley erklärt das Konzept der "referenziellen Transparenz" in [seiner Keynote auf der UseR-Konferenz 2016] (https://channel9.msdn.com/Events/useR-international-R-User-conference/useR2016/Towards-a-grammar -von-interaktiven-Grafiken) (bei 38min30s)."Formel halten Sie sowohl den Code als auch die Umgebung, in der dieser Code ausgewertet werden soll, ohne tatsächlich die Auswertung durchzuführen." Ich habe ein Beispiel mit einer Formel erstellt und in eine neue Antwort eingefügt. –

0

in his 2016 UseR talk (@ 38min30s), erklärt Hadley Wickham das Konzept der referential transparency. Mit Hilfe einer Formel kann die Filterfunktion wie neu gefasst:

nrowspecies5 <- function(dtf, formula){ 
    dtf %>% 
     filter_(formula) %>% 
     nrow() 
} 

Dies hat den zusätzlichen Vorteil, beeing allgemeineren

nrowspecies5(iris, ~ species == "versicolor") 
# 50 
nrowspecies5(iris, ~ sepal.length > 6 & species == "virginica") 
# 41 
nrowspecies5(iris, ~ sepal.length > 6 & species == "setosa") 
# 0 
Verwandte Themen