2016-02-04 16 views
9

Ich habe Probleme bei der Refactoring dplyr in einer Weise, die nicht-Standard-Bewertung erhält. Sagen wir, ich möchte eine Funktion erstellen, die immer auswählt und umbenennt.Programmierung mit dplyr und lazyeval

library(lazyeval) 
library(dplyr) 

df <- data.frame(a = c(1,2,3), f = c(4,5,6), lm = c(7, 8 , 9)) 

select_happy<- function(df, col){ 
    col <- lazy(col) 
    fo <- interp(~x, x=col) 
    select_(df, happy=fo) 
} 

f <- function(){ 
    print('foo') 
} 

select_happy() geschrieben nach der Antwort auf diesen Beitrag Refactor R code when library functions use non-standard evaluation. select_happy() arbeitet mit Spaltennamen, die entweder nicht definiert oder in der globalen Umgebung definiert sind. Es treten jedoch Probleme auf, wenn ein Spaltenname auch der Name einer Funktion in einem anderen Namespace ist.

select_happy(df, a) 
# happy 
# 1  1 
# 2  2 
# 3  3 

select_happy(df, f) 
# happy 
# 1  4 
# 2  5 
# 3  6 

select_happy(df, lm) 
# Error in eval(expr, envir, enclos) (from #4) : object 'datafile' not found 

environment(f) 
# <environment: R_GlobalEnv> 

environment(lm) 
# <environment: namespace:stats> 

Aufruf lazy() auf f und lm zeigt einen Unterschied in dem faulen Objekt, in dem die Funktionsdefinition für lm wird in dem faulen Objekt erscheinen und für f es ist nur der Name der Funktion.

substitute scheint mit lm zu arbeiten.

select_happy<- function(df, col){ 
    col <- substitute(col) # <- substitute() instead of lazy() 
    fo <- interp(~x, x=col) 
    select_(df, happy=fo) 
} 

select_happy(df, lm) 
# happy 
# 1  7 
# 2  8 
# 3  9 

Doch nach the vignette on lazyeval Lesen scheint es, dass lazy als überlegen Ersatz für substitute dienen sollte. Darüber hinaus funktioniert die reguläre select Funktion gut.

select(df, happy=lm) 
# happy 
# 1  7 
# 2  8 
# 3  9 

Meine Frage ist, wie kann ich select_happy() so schreiben, dass es in allen Wegen, die funktioniert select() tun? Es fällt mir schwer, mich in die Scoping- und Nicht-Standard-Evaluierung zu vertiefen. Allgemeiner ausgedrückt: Was wäre eine solide Strategie für die Programmierung mit dplyr, die diese und andere Probleme vermeiden könnte?

bearbeiten

testete ich docendo discimus-Lösung aus und es funktionierte großartig, aber ich würde es gerne wissen, ob ein Weg, um Argumente zu verwenden, anstatt Punkte, für die Funktion. Ich denke, es ist auch wichtig, interp() verwenden zu können, da Sie möglicherweise Eingaben in eine kompliziertere Formel eingeben möchten, wie in dem Beitrag, mit dem ich früher verlinkt habe. Ich denke, dass der Kern des Problems auf die Tatsache zurückzuführen ist, dass lazy_dots() den Ausdruck anders erfasst als lazy(). Ich würde gerne verstehen, warum sie sich anders verhalten, und wie man lazy() verwendet, um die gleiche Funktionalität wie lazy_dots() zu erhalten.

g <- function(...){ 
    lazy_dots(...) 
} 

h <- function(x){ 
    lazy(x) 
} 

g(lm)[[1]] 
# <lazy> 
# expr: lm 
# env: <environment: R_GlobalEnv> 
h(lm) 
# <lazy> 
# expr: function (formula, data, subset, weights, na.action, method = "qr", ... 
# env: <environment: R_GlobalEnv> 

Auch .follow__symbols-FALSE für lazy() zu ändern, so dass es die gleiche wie lazy_dots() ist nicht funktioniert.

lazy 
# function (expr, env = parent.frame(), .follow_symbols = TRUE) 
# { 
#  .Call(make_lazy, quote(expr), environment(), .follow_symbols) 
# } 
# <environment: namespace:lazyeval> 

lazy_dots 
# function (..., .follow_symbols = FALSE) 
# { 
#  if (nargs() == 0) 
#   return(structure(list(), class = "lazy_dots")) 
#  .Call(make_lazy_dots, environment(), .follow_symbols) 
# } 
# <environment: namespace:lazyeval> 


h2 <- function(x){ 
    lazy(x, .follow_symbols=FALSE) 
} 

h2(lm) 
# <lazy> 
# expr: x 
# env: <environment: 0xe4a42a8> 

Ich fühle mich nur irgendwie fest, was zu tun ist.

+0

@Henrik das ist, was ich wollte! Es wird jedoch immer noch ein Fehler ausgegeben, und insgesamt ist das Problem das gleiche. Ich habe die Frage aktualisiert, um die Korrektur widerzuspiegeln. –

Antwort

2

Eine Option kann zu schreiben select_happy fast die gleiche Art und Weise wie die Standard select Funktion machen:

select_happy<- function(df, ...){ 
    select_(df, .dots = setNames(lazy_dots(...), "happy")) 
} 

f <- function(){ 
    print('foo') 
} 

> select_happy(df, a) 
    happy 
1  1 
2  2 
3  3 
> 
> select_happy(df, f) 
    happy 
1  4 
2  5 
3  6 
> 
> select_happy(df, lm) 
    happy 
1  7 
2  8 
3  9 

Beachten Sie, dass die Funktionsdefinition der Standard select Funktion ist:

> select 
function (.data, ...) 
{ 
    select_(.data, .dots = lazyeval::lazy_dots(...)) 
} 
<environment: namespace:dplyr> 

Beachten Sie auch, dass nach dieser Definition select_happy mehrere zu wählende Spalten akzeptiert, aber alle zusätzlichen Spalten "NA" nennt:

Natürlich können Sie einige Änderungen für solche Fälle machen, zum Beispiel:

select_happy<- function(df, ...){ 
    dots <- lazy_dots(...) 
    n <- length(dots) 
    if(n == 1) newnames <- "happy" else newnames <- paste0("happy", seq_len(n)) 
    select_(df, .dots = setNames(dots, newnames)) 
} 

> select_happy(df, f) 
    happy 
1  4 
2  5 
3  6 

> select_happy(df, lm, a) 
    happy1 happy2 
1  7  1 
2  8  2 
3  9  3 
+0

Diese Lösung funktioniert, aber ich mag es nicht, dass es die Verwendung der Ellipse erzwingt. Die Vignette lässt es so aussehen, als sollte dies eine gültige Verwendung von 'faul()' sein. Für Funktionen, die die Ellipse verwenden müssen, scheint dies jedoch eine gute Lösung zu sein. –