请考虑以下列中的列选择data.table:
library(data.table) # using 1.8.7 from r-forge
dt <- data.table(a = 1:5, b = i <- rnorm(5), c = pnorm(i))
dt[, list(a,b)] #ok
Run Code Online (Sandbox Code Playgroud)
为了在具有许多和可变列的某些计算中简化我的代码,我想list(a,b)用函数替换.这是第一次尝试:
.ab <- function() quote(list(a, b))
dt[, eval(.ab())] #ok - same as above
Run Code Online (Sandbox Code Playgroud)
理想情况下,我想摆脱eval()来自[.data.table呼叫,它限制在定义.ab的同时避免将数据表,而dt该函数.ab.
.eab <- function() eval(quote(list(a, b)))
dt[, .eab()]
# Error in eval(expr, envir, enclos) : object 'b' not found
Run Code Online (Sandbox Code Playgroud)
发生了什么?怎么解决这个问题?
我怀疑是什么让我感到愤怒的是R的词汇范围以及正确的评估list(a,b)依赖于它在数据表的J环境中dt.唉,我不知道如何获取对正确环境的引用,并将其用作envir或enclos参数dt.
# .eab <- function() eval(quote(list(a, b)), envir = ?, enclos = ?)
Run Code Online (Sandbox Code Playgroud)
编辑
这种方法几乎可行:
.eab <- function(e) eval(quote(list(a, b)), envir = e)
dt[, .eab(dt)]
Run Code Online (Sandbox Code Playgroud)
有两个缺点:(1)不返回列名,(2)dt必须明确传递(我宁愿避免).我宁愿避免硬编码dt作为选择环境.这些考虑导致了另一种提出上述问题的方式:是否有一种dt从内部获取环境的程序化方法.eab?
目的是创建一个表达式而不是一个函数。
DT[, list(a,b), by=...] # ok
.ab = quote(list(a, b)) # simpler here, no need for function()
DT[, eval(.ab), by=...] # same
Run Code Online (Sandbox Code Playgroud)
这种方法是data.table中分组快速的原因之一:j在静态环境中对所有组进行评估,因此可以避免每个函数调用的(较小)开销。
但是,如果.ab由于某种原因确实需要将其作为函数,那么我们当然可以进一步考虑一下。
警告,如果内部机制发生[.data.table变化,这可能不稳健、缓慢和/或容易中断,但如果由于某种原因无法解决它,这里有一个函数似乎可以满足您的要求。by我还可以想象,如果您开始使用其他选项(例如中的),它就不起作用[.data.table。
.eab <- function() {
foo <- quote(list(a,b))
ans <- eval(foo, envir = parent.frame(3)$x)
names(ans) <- vapply(as.list(foo)[-1], deparse, character(1))
ans
}
identical(dt[, .eab()], dt[, list(a,b)])
# TRUE
Run Code Online (Sandbox Code Playgroud)
同样,这会颠覆/减少大量有充分理由存在的代码。