2016-02-04 22 views
9

非標準の評価を維持する方法でdplyrをリファクタリングする際に問題が発生しています。常に選択して名前を変更する関数を作成したいとします。dplyrと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()この記事Refactor R code when library functions use non-standard evaluationへの答えに応じて書かれています。 select_happy()は、未定義またはグローバル環境で定義されている列名で機能します。ただし、列名が別の名前空間内の関数の名前でもある場合は、問題になります。 FとLMのlazy()を呼び出す

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> 

は、LMのための関数定義は怠惰なオブジェクトに表示されている怠惰なオブジェクトの差を示しており、Fのためには、関数の名前だけです。

lazy(f) 
# <lazy> 
# expr: f 
# env: <environment: R_GlobalEnv> 

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

substituteがlmで動作するようです。

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 

しかし、the vignette on lazyevalを読んだ後、lazysubstituteのための優れた代替として使用すべきであると思われます。さらに、通常のselect関数は正常に機能します。

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

私の質問はどのように私はそれがselect()が行うすべての方法で動作するようにselect_happy()を書くことができるのですか?私はスコープと非標準的な評価の周りに私の頭を包んで苦労している。より一般的には、これらの問題やその他の問題を回避できるdplyrを使ったプログラミングのための強固な戦略は何でしょうか?

編集

私はdocendoのdiscimusのソリューションをテストし、それは素晴らしい仕事が、私は機能のために、むしろドットよりも、引数を使用する方法があるかどうかを知りたいです。 interp()を使用できることも重要だと思います。これは、先にリンクした記事のように、入力をより複雑な数式に入力したい場合があるためです。問題の核心は、とは違ってlazy_dots()が式を取り込んでいるという事実になると私は思う。私はなぜ彼らが異なったふるまいをしているのか、lazy()を使用してlazy_dots()と同じ機能を利用する方法を理解したいと思います。

lazy_dots()が動作しないと、それは同じになるようにしても lazy()ため FALSE.follow__symbolsを変更
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> 

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> 

私はちょうど何をすべきかについて固執しています。

一つのオプションは、書き込み select_happy標準 select機能とほぼ同じようにするかもしれ
+0

@Henrik私が何を意味するのかであります!それでもエラーは出力され、問題は全体的に同じです。私は修正を反映するために質問を更新しました。 –

答えて

2

:標準select関数の関数定義があることを

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 

注:

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

も注意をこの定義では、select_happyは選択する複数の列を受け入れますが、追加の列には「NA」という名前を付けます:

あなたは、たとえば、そのような場合のためにいくつかの変更を作ることができる。もちろん、
> select_happy(df, lm, a) 
    happy NA 
1  7 1 
2  8 2 
3  9 3 

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

この解決策は機能しますが、私は省略記号の使用を強制するのが好きではありません。このビネットは 'lazy()'を有効に使うべきように見えます。省略記号を使用する必要のある関数については、これは良い解決策のようです。 –

関連する問題