为什么 `substitute` 在多行中起作用,但在一行中不起作用?
Why does `substitute` work in multiple lines, but not in a single line?
我试图回答 关于为 data.table 对象创建非标准评估函数,进行分组求和。 Akrun 想出了一个可爱的答案,我将在这里简化它:
akrun <- function(data, var, group){
var <- substitute(var)
group <- substitute(group)
data[, sum(eval(var)), by = group]
}
library(data.table)
mt = as.data.table(mtcars)
akrun(mt, cyl, mpg)
# group V1
# 1: 6 138.2
# 2: 4 293.3
# 3: 8 211.4
我也在研究一个答案,并且得到了接近相同的答案,但是 substitute
s 与其他答案内嵌。我的导致错误:
gregor = function(data, var, group) {
data[, sum(eval(substitute(var))), by = substitute(group)]
}
gregor(mt, mpg, cyl)
# Error in `[.data.table`(data, , sum(eval(substitute(var))), by = substitute(group)) :
# 'by' or 'keyby' must evaluate to vector or list of vectors
# (where 'list' includes data.table and data.frame which are lists, too)
从表面上看,我的功能只是 Akrun 的简单替代。为什么它不起作用?
请注意,两个替换都会导致问题,如下所示:
gregor_1 = function(data, var, group) {
var = substitute(var)
data[,sum(eval(var)),
by = substitute(group)]
}
gregor_1(mt, mpg, cyl)
# Same error as above
gregor_2 = function(data, var, group) {
group = substitute(group)
data[,sum(eval(substitute(var))),
by = group]
}
gregor_2(mt, mpg, cyl)
# Error in eval(substitute(var)) : object 'mpg' not found
似乎 substitute
在 data table 中的工作方式与人们期望它在其他上下文中的工作方式不同,但您可以使用 rlang 包中的 enexpr
substitute
的位置:
library(data.table)
library(rlang)
gregor_rlang = function(data, var, group) {
data[, sum(eval(enexpr(var))), by = .(group = eval(enexpr(group)))]
}
gregor_rlang(mt, mpg, cyl)
## group V1
## 1: 6 138.2
## 2: 4 293.3
## 3: 8 211.4
环境
这个问题似乎与环境有关,因为这在我们特别给出了 substitute
应该使用的环境的地方有效。
gregor_pf = function(data, val, group) {
data[, sum(eval(substitute(val, parent.env(environment())))),
by = c(deparse(substitute(group)))]
}
gregor_pf(mt, mpg, cyl)
## cyl V1
## 1: 6 138.2
## 2: 4 293.3
## 3: 8 211.4
在 substitute
的文档中,您可以阅读它如何决定要替换的内容,
事实上,默认情况下,它会搜索调用它的环境。
如果您在 data.table
框架内调用 substitute
(即在 []
内)
它将无法找到符号,因为它们不存在于 data.table
评估环境中,
他们在调用 [
的环境中。
您可以"invert"调用函数的顺序以获得您想要的行为:
library(data.table)
foo <- function(dt, group, var) {
eval(substitute(dt[, sum(var), by = group]))
}
foo(as.data.table(mtcars), cyl, mpg)
cyl V1
1: 6 138.2
2: 4 293.3
3: 8 211.4
data.table 使用 NSE,因为它需要 analyse/manipulate by
参数,然后再选择是否对其求值(如果你给它一个符号,例如它不会评估它)。
结果是,如果需要评估参数,则应在正确的环境中对其进行评估,这是函数的责任。 data.table
在数据中评估其 by
参数,而不是在调用环境中。
在大多数情况下,您看不到这个问题,因为如果找不到符号,将在父环境中对其进行评估,但 substitute()
更敏感。
参见下面的示例:
fun <- function(x){
standard_eval(x)
non_standard_eval_safe(x)
non_standard_eval_not_safe(x)
}
standard_eval <- function(expr) print(expr)
non_standard_eval_safe <- function(expr) {
expr <- bquote(print(.(substitute(expr)))) # will be quote(print(x)) in our example
eval.parent(expr)
}
non_standard_eval_not_safe <- function(expr) {
expr <- bquote(print(.(substitute(expr)))) # will be quote(print(x)) in our example
eval(expr)
}
standard_eval(1+1)
#> [1] 2
non_standard_eval_safe(1+1)
#> [1] 2
non_standard_eval_not_safe(1+1)
#> [1] 2
fun(1+1)
#> [1] 2
#> [1] 2
#> Error in print(x): object 'x' not found
由 reprex package (v0.3.0)
于 2020 年 2 月 20 日创建
我试图回答
akrun <- function(data, var, group){
var <- substitute(var)
group <- substitute(group)
data[, sum(eval(var)), by = group]
}
library(data.table)
mt = as.data.table(mtcars)
akrun(mt, cyl, mpg)
# group V1
# 1: 6 138.2
# 2: 4 293.3
# 3: 8 211.4
我也在研究一个答案,并且得到了接近相同的答案,但是 substitute
s 与其他答案内嵌。我的导致错误:
gregor = function(data, var, group) {
data[, sum(eval(substitute(var))), by = substitute(group)]
}
gregor(mt, mpg, cyl)
# Error in `[.data.table`(data, , sum(eval(substitute(var))), by = substitute(group)) :
# 'by' or 'keyby' must evaluate to vector or list of vectors
# (where 'list' includes data.table and data.frame which are lists, too)
从表面上看,我的功能只是 Akrun 的简单替代。为什么它不起作用?
请注意,两个替换都会导致问题,如下所示:
gregor_1 = function(data, var, group) {
var = substitute(var)
data[,sum(eval(var)),
by = substitute(group)]
}
gregor_1(mt, mpg, cyl)
# Same error as above
gregor_2 = function(data, var, group) {
group = substitute(group)
data[,sum(eval(substitute(var))),
by = group]
}
gregor_2(mt, mpg, cyl)
# Error in eval(substitute(var)) : object 'mpg' not found
似乎 substitute
在 data table 中的工作方式与人们期望它在其他上下文中的工作方式不同,但您可以使用 rlang 包中的 enexpr
substitute
的位置:
library(data.table)
library(rlang)
gregor_rlang = function(data, var, group) {
data[, sum(eval(enexpr(var))), by = .(group = eval(enexpr(group)))]
}
gregor_rlang(mt, mpg, cyl)
## group V1
## 1: 6 138.2
## 2: 4 293.3
## 3: 8 211.4
环境
这个问题似乎与环境有关,因为这在我们特别给出了 substitute
应该使用的环境的地方有效。
gregor_pf = function(data, val, group) {
data[, sum(eval(substitute(val, parent.env(environment())))),
by = c(deparse(substitute(group)))]
}
gregor_pf(mt, mpg, cyl)
## cyl V1
## 1: 6 138.2
## 2: 4 293.3
## 3: 8 211.4
在 substitute
的文档中,您可以阅读它如何决定要替换的内容,
事实上,默认情况下,它会搜索调用它的环境。
如果您在 data.table
框架内调用 substitute
(即在 []
内)
它将无法找到符号,因为它们不存在于 data.table
评估环境中,
他们在调用 [
的环境中。
您可以"invert"调用函数的顺序以获得您想要的行为:
library(data.table)
foo <- function(dt, group, var) {
eval(substitute(dt[, sum(var), by = group]))
}
foo(as.data.table(mtcars), cyl, mpg)
cyl V1
1: 6 138.2
2: 4 293.3
3: 8 211.4
data.table 使用 NSE,因为它需要 analyse/manipulate by
参数,然后再选择是否对其求值(如果你给它一个符号,例如它不会评估它)。
结果是,如果需要评估参数,则应在正确的环境中对其进行评估,这是函数的责任。 data.table
在数据中评估其 by
参数,而不是在调用环境中。
在大多数情况下,您看不到这个问题,因为如果找不到符号,将在父环境中对其进行评估,但 substitute()
更敏感。
参见下面的示例:
fun <- function(x){
standard_eval(x)
non_standard_eval_safe(x)
non_standard_eval_not_safe(x)
}
standard_eval <- function(expr) print(expr)
non_standard_eval_safe <- function(expr) {
expr <- bquote(print(.(substitute(expr)))) # will be quote(print(x)) in our example
eval.parent(expr)
}
non_standard_eval_not_safe <- function(expr) {
expr <- bquote(print(.(substitute(expr)))) # will be quote(print(x)) in our example
eval(expr)
}
standard_eval(1+1)
#> [1] 2
non_standard_eval_safe(1+1)
#> [1] 2
non_standard_eval_not_safe(1+1)
#> [1] 2
fun(1+1)
#> [1] 2
#> [1] 2
#> Error in print(x): object 'x' not found
由 reprex package (v0.3.0)
于 2020 年 2 月 20 日创建