Dplyr的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
......但是这样的&#34;天真的&#34;实现不起作用,mutate_impl
背后的C ++代码非常复杂。我知道它不起作用,因为lazy_eval
上"lazy_dots"
使用lapply
,即每个表达式都是相互独立评估的,而我宁愿需要链式评估才能返回结果回到共享环境。如何使它工作?
答案 0 :(得分:2)
我不完全确定它是你想要的,但是这里有3个基本R的mutate克隆,可以用你的例子:
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
答案 1 :(得分:0)
在阅读 Moody_Mudskipper 的回答后,我推出了自己的解决方案,重新实现了lazyeval::lazy_eval
函数,以获取“记住”过去评估的表达式列表:
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
中的my_mutate
需要替换为my_eval
才能使所有内容按预期工作。