我在以保留非标准评估的方式重构 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()
是根据这个帖子的答案写的当库函数使用非标准评估时重构 R 代码 https://stackoverflow.com/questions/26058182/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>
Calling lazy()
on f 和 lm 显示了惰性对象的差异,其中 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
然而读完之后小插图上lazyeval https://cran.r-project.org/web/packages/lazyeval/vignettes/lazyeval.html看起来lazy
应该作为更好的替代品substitute
。此外,常规的select
功能运行得很好。
select(df, happy=lm)
# happy
# 1 7
# 2 8
# 3 9
我的问题是我该如何写select_happy()
以便它能以各种方式发挥作用select()
做?我很难理解范围界定和非标准评估。更一般地说,使用 dplyr 进行编程可以避免这些问题和其他问题的可靠策略是什么?
Edit
我测试了 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>
甚至改变.follow__symbols
to FALSE
for lazy()
所以它与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>
我只是觉得不知道该做什么。