2017-07-14 21 views
0

Dplyrのmutate関数は "連鎖"式を評価できます。どのようにmutateのようなチェーン評価を実装するには?

library(dplyr) 

data.frame(a = 1) %>% 
    mutate(b = a + 1, c = b * 2) 
## a b c 
## 1 1 2 4 

これはどのように実装できますか? dplyrのソースコードで一目は、候補コードの基本的な構造を明らかにする:

library(lazyeval) 
library(rlang) 

compat_as_lazy <- function(quo) { 
    structure(class = "lazy", list(
    expr = f_rhs(quo), 
    env = f_env(quo) 
)) 
} 

compat_as_lazy_dots <- function(...) { 
    structure(class = "lazy_dots", lapply(quos(...), compat_as_lazy)) 
} 

my_mutate <- function(.data, ...) { 
    lazy_eval(compat_as_lazy_dots(...), data = .data) 
} 

data.frame(a = 1) %>% 
    my_mutate(b = a + 1, c = b * 2) 
## Error in eval(x$expr, data, x$env) : object 'b' not found 

を...しかし、そのような「ナイーブ」実装では動作しないとmutate_impl背後にあるC++のコードはかなり複雑です。 lazy_eval"lazy_dots"にはlapplyが使用されています。つまり、それぞれの式は互いに独立して評価されますが、結果を共有環境に戻して連鎖的に評価する必要があります。それを動作させるには?

+0

ああ、あなた自身の突然変異関数を作成しようとしています.... – CPak

答えて

2

私はあなたの例で動作するベースR 3個のmutateクローンそれはあなたが欲しいものだ完全にはよく分からないが、ここにある:私は自分で出てきたMoody_Mudskipperの答えを読んだ後

mutate_transform <- function(df,...){ 
    lhs <- names(match.call())[-1:-2] 
    rhs <- as.character(substitute(list(...)))[-1] 
    args = paste(lhs,"=",rhs) 
    for(arg in args){ 
    df <- eval(parse(text=paste("transform(df,",arg,")"))) 
    } 
df 
} 

mutate_within <- function(df,...){ 
    lhs <- names(match.call())[-1:-2] 
    rhs <- as.character(substitute(list(...)))[-1] 
    args = paste(lhs,"=",rhs) 
    df <- eval(parse(text=paste("within(df,{",paste(args,collapse=";"),"})"))) 
    df 
} 

mutate_attach <- function(df,...){ 
    lhs <- names(match.call())[-1:-2] 
    rhs <- as.character(substitute(list(...)))[-1] 
    new_env <- new.env() 
    with(data = new_env,attach(df,warn.conflicts = FALSE)) 
    for(i in 1:length(lhs)){ 
    assign(lhs[i],eval(parse(text=rhs[i]),envir=new_env),envir=new_env) 
    } 
    add_vars <- setdiff(lhs,names(df)) 
    with(data = new_env,detach(df)) 
    for(var in add_vars){ 
    df[[var]] <- new_env[[var]] 
    } 
    df 
} 

data.frame(a = 1) %>% mutate_transform(b = a + 1, c = b * 2) 
# a b c 
# 1 1 2 4 
data.frame(a = 1) %>% mutate_within(b = a + 1, c = b * 2) 
# a c b <--- order is different here 
# 1 1 4 2 
data.frame(a = 1) %>% mutate_attach(b = a + 1, c = b * 2) 
# a b c 
# 1 1 2 4 
0

をであることをmy_mutateニーズに

my_eval <- function(expr, .data = NULL) { 
    idx <- structure(seq_along(expr), 
        names = names(expr)) 
    lapply(idx, function(i) { 
    evl <- lazy_eval(expr[[i]], data = .data) 
    .data[names(expr)[i]] <<- evl 
    evl 
    }) 
} 

次に、lazy_eval:過去評価を「記憶」という表現のリストについてlazyeval::lazy_eval機能が再実装ソリューション期待どおりに動作するためにはmy_evalに置き換えてください。