【发布时间】:2016-02-04 08:01:34
【问题描述】:
我在以保留非标准评估的方式重构 dplyr 时遇到问题。假设我想创建一个始终选择和重命名的函数。
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() 适用于未定义或在全局环境中定义的列名。但是,当列名也是另一个命名空间中的函数名时,就会遇到问题。
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>
在 f 和 lm 上调用 lazy() 会显示惰性对象中的差异,其中 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 之后,lazy 似乎应该作为substitute 的更好替代品。此外,常规的 select 函数也可以正常工作。
select(df, happy=lm)
# happy
# 1 7
# 2 8
# 3 9
我的问题是如何编写 select_happy() 以便它以 select() 的所有方式工作?我很难理解范围界定和非标准评估。更一般地说,使用 dplyr 编程可以避免这些和其他问题的可靠策略是什么?
编辑
我测试了 docendo discimus 的解决方案,效果很好,但我想知道是否有一种方法可以为函数使用参数而不是点。我认为能够使用interp() 也很重要,因为您可能希望将输入输入到更复杂的公式中,就像我之前链接到的帖子中一样。我认为问题的核心在于lazy_dots() 捕获的表达式与lazy() 不同。我想了解他们为什么表现不同,以及如何使用lazy() 来获得与lazy_dots() 相同的功能。
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() 的.follow__symbols 更改为FALSE 以使其与lazy_dots() 相同也不起作用。
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>
我真的有点不知道该怎么做。
【问题讨论】:
-
@Henrik 这就是我的意思!虽然它仍然输出错误,但总的来说问题是一样的。我已更新问题以反映更正。
标签: r dplyr lazy-evaluation