NSE lazyeval::lazy 与引用变量名时的替换
NSE lazyeval::lazy vs. substitute when referring to variable names
我仍在努力思考非标准评估及其在 dplyr
中的使用方式。我无法理解为什么当函数参数是变量名时惰性求值很重要,因此原始上下文的环境似乎并不重要。
在下面的代码中,函数 select3()
使用惰性计算,但失败了(我相信),因为它试图跟随变量名 order
一直到 base::order
。
是否可以像我在 select4()
中那样使用替代品,或者是否有其他方法可以实现此功能?什么时候保存原始环境才真正重要,什么时候我真的希望这些参数引用变量?
谢谢!
library(dplyr)
library(lazyeval)
# Same as dplyr::select
select2 <- function(.data, ...) {
select_(.data, .dots = lazy_dots(...))
}
# I want to have two capture groups of variables, so I need named arguments.
select3 <- function(.data, group1, group2) {
out1 <- select_(.data, .dots = lazy(group1))
out2 <- select_(.data, .dots = lazy(group2))
list(out1, out2)
}
df <- data.frame(x = 1:2, y = 3:4, order = 5:6)
# select3 seems okay at first...
df %>% select2(x, y)
df %>% select3(x, y)
# But fails when the variable is a function defined in the namespace
df %>% select2(x, order)
df %>% select3(x, order)
# Error in eval(expr, envir, enclos) : object 'datafile' not found
# Using substitute instead of lazy works. But I'm not sure I understand the
# implications of doing this.
select4 <- function(.data, group1, group2) {
out1 <- select_(.data, .dots = substitute(group1))
out2 <- select_(.data, .dots = substitute(group2))
list(out1, out2)
}
df %>% select4(x, order)
PS 在相关说明中,这是错误还是预期行为?
select(df, z)
# Error in eval(expr, envir, enclos) : object 'z' not found
# But if I define z as a numeric variable it works.
z <- 1
select(df, z)
更新
一个。 Webb 在下面指出环境对 select
很重要,因为像 one_of
这样的特殊函数可以使用它的对象。
更新 2
我曾经有一个丑陋的 hack 作为修复,但这里有一个更好的方法;我应该知道甚至 lazy
也有一个标准的评估对应部分 lazy_
select6 <- function(.data, group1, group2) {
g1 <- lazy_(substitute(group1), env = parent.frame())
g2 <- lazy_(substitute(group2), env = parent.frame())
out1 <- select_(.data, .dots = g1)
out2 <- select_(.data, .dots = g2)
list(out1, out2)
}
# Or even more like the original...
lazy_parent <- function(expr) {
# Need to go up twice, because lazy_parent creates an environment for itself
e1 <- substitute(expr)
e2 <- do.call("substitute", list(e1), envir = parent.frame(1))
lazy_(e2, parent.frame(2))
}
select7 <- function(.data, group1, group2) {
out1 <- select_(.data, .dots = lazy_parent(group1))
out2 <- select_(.data, .dots = lazy_parent(group2))
list(out1, out2)
}
这里的问题是 lazy
默认遵循 promise,而 order
是由于延迟加载包而导致的 promise。
library(pryr)
is_promise(order)
#> TRUE
lazy_dots
的默认值与 select
中使用的相反。
但是这里也发生了其他事情,其中特殊 ...
的性质用于提取未评估的表达式。虽然您在许多情况下都可以使用替代品,但尝试通过 select
重命名为可用会失败。
select4(df,foo=x,bar=order)
#> Error in select4(df, foo = x, bar = order) :
#> unused arguments (foo = x, bar = order)
但是,这有效
select5 <- function(.data, ...) {
dots<-lazy_dots(...)
out1 <- select_(.data, .dots=dots[1])
out2 <- select_(.data, .dots=dots[2])
list(out1, out2)
}
select5(df,foo=x,bar=order)
#> [[1]]
#> foo
#> 1 1
#> 2 2
#>
#> [[2]]
#> bar
#> 1 5
#> 2 6
再举个例子,substitute
失败更直接,因为没有承载环境,考虑
vars<-c("x","y")
select4(df,one_of(vars),order)
#>Error in one_of(vars, ...) : object 'vars' not found
select5(df,one_of(vars),order)
#> [[1]]
#> x y
#> 1 1 3
#> 2 2 4
#>
#> [[2]]
#> order
#> 1 5
#> 2 6
select4
版本失败,因为它找不到 vars
,其中 select5
由于 lazy_dots
携带环境而成功。注意 select4(df,one_of(c("x","y")),order)
没问题,因为它使用文字。
我仍在努力思考非标准评估及其在 dplyr
中的使用方式。我无法理解为什么当函数参数是变量名时惰性求值很重要,因此原始上下文的环境似乎并不重要。
在下面的代码中,函数 select3()
使用惰性计算,但失败了(我相信),因为它试图跟随变量名 order
一直到 base::order
。
是否可以像我在 select4()
中那样使用替代品,或者是否有其他方法可以实现此功能?什么时候保存原始环境才真正重要,什么时候我真的希望这些参数引用变量?
谢谢!
library(dplyr)
library(lazyeval)
# Same as dplyr::select
select2 <- function(.data, ...) {
select_(.data, .dots = lazy_dots(...))
}
# I want to have two capture groups of variables, so I need named arguments.
select3 <- function(.data, group1, group2) {
out1 <- select_(.data, .dots = lazy(group1))
out2 <- select_(.data, .dots = lazy(group2))
list(out1, out2)
}
df <- data.frame(x = 1:2, y = 3:4, order = 5:6)
# select3 seems okay at first...
df %>% select2(x, y)
df %>% select3(x, y)
# But fails when the variable is a function defined in the namespace
df %>% select2(x, order)
df %>% select3(x, order)
# Error in eval(expr, envir, enclos) : object 'datafile' not found
# Using substitute instead of lazy works. But I'm not sure I understand the
# implications of doing this.
select4 <- function(.data, group1, group2) {
out1 <- select_(.data, .dots = substitute(group1))
out2 <- select_(.data, .dots = substitute(group2))
list(out1, out2)
}
df %>% select4(x, order)
PS 在相关说明中,这是错误还是预期行为?
select(df, z)
# Error in eval(expr, envir, enclos) : object 'z' not found
# But if I define z as a numeric variable it works.
z <- 1
select(df, z)
更新
一个。 Webb 在下面指出环境对 select
很重要,因为像 one_of
这样的特殊函数可以使用它的对象。
更新 2
我曾经有一个丑陋的 hack 作为修复,但这里有一个更好的方法;我应该知道甚至 lazy
也有一个标准的评估对应部分 lazy_
select6 <- function(.data, group1, group2) {
g1 <- lazy_(substitute(group1), env = parent.frame())
g2 <- lazy_(substitute(group2), env = parent.frame())
out1 <- select_(.data, .dots = g1)
out2 <- select_(.data, .dots = g2)
list(out1, out2)
}
# Or even more like the original...
lazy_parent <- function(expr) {
# Need to go up twice, because lazy_parent creates an environment for itself
e1 <- substitute(expr)
e2 <- do.call("substitute", list(e1), envir = parent.frame(1))
lazy_(e2, parent.frame(2))
}
select7 <- function(.data, group1, group2) {
out1 <- select_(.data, .dots = lazy_parent(group1))
out2 <- select_(.data, .dots = lazy_parent(group2))
list(out1, out2)
}
这里的问题是 lazy
默认遵循 promise,而 order
是由于延迟加载包而导致的 promise。
library(pryr)
is_promise(order)
#> TRUE
lazy_dots
的默认值与 select
中使用的相反。
但是这里也发生了其他事情,其中特殊 ...
的性质用于提取未评估的表达式。虽然您在许多情况下都可以使用替代品,但尝试通过 select
重命名为可用会失败。
select4(df,foo=x,bar=order)
#> Error in select4(df, foo = x, bar = order) :
#> unused arguments (foo = x, bar = order)
但是,这有效
select5 <- function(.data, ...) {
dots<-lazy_dots(...)
out1 <- select_(.data, .dots=dots[1])
out2 <- select_(.data, .dots=dots[2])
list(out1, out2)
}
select5(df,foo=x,bar=order)
#> [[1]]
#> foo
#> 1 1
#> 2 2
#>
#> [[2]]
#> bar
#> 1 5
#> 2 6
再举个例子,substitute
失败更直接,因为没有承载环境,考虑
vars<-c("x","y")
select4(df,one_of(vars),order)
#>Error in one_of(vars, ...) : object 'vars' not found
select5(df,one_of(vars),order)
#> [[1]]
#> x y
#> 1 1 3
#> 2 2 4
#>
#> [[2]]
#> order
#> 1 5
#> 2 6
select4
版本失败,因为它找不到 vars
,其中 select5
由于 lazy_dots
携带环境而成功。注意 select4(df,one_of(c("x","y")),order)
没问题,因为它使用文字。