data.table ..用j中的函数表示

EKt*_*age 10 r data.table

我试图使用data.table的..函数表示法,这是我到目前为止的代码:

set.seed(42)
dt <- data.table(
  x = rnorm(10),
  y = runif(10)
)

test_func <- function(data, var, var2) {
  vars <- c(var, var2)
  data[, ..vars]
}

test_func(dt, 'x', 'y') # this works

test_func2 <- function(data, var, var2) {
  data[, ..var]
}

test_func2(dt, 'x', 'y') # this works too

test_func3 <- function(data, var, var2) {
  data[, sum(..var)]
}

test_func3(dt, 'x', 'y') 
# this does not work
# Error in eval(jsub, SDenv, parent.frame()) : object '..var' not found
Run Code Online (Sandbox Code Playgroud)

一旦它包含在另一个函数中,它似乎data.table无法识别.我知道我可以用它来实现结果,但我想知道我在大多数情况下都在使用最佳实践...jsum(get(var))

Mat*_*ill 2

鹦鹉学舌地重复另一个同样适用于这里的问题的答案。这不是最漂亮的解决方案,但它的变体过去曾多次为我工作过。

感谢@Frankparse()在这里提供非解决方案!

我很熟悉这句古老的格言:“如果答案是 parse(),你通常应该重新思考这个问题。” ,但是在调用环境中进行评估时,我很难多次想出替代方案data.table,我希望看到一个健壮的解决方案,它不会执行作为字符串传入的任意代码。事实上,我发布这样的答案的一半原因是希望有人可以推荐更好的选择。

test_func3 <- function(data, var, var2) {
  expr = substitute(sum(var), list(var=as.symbol(var)))
  data[, eval(expr)]
}

test_func3(dt, 'x', 'y')
## [1] 5.472968
Run Code Online (Sandbox Code Playgroud)

关于使用 eval(parse(...)) 可能出现的假设世界末日场景的快速免责声明

关于 的危险还有更深入的讨论eval(parse(...)),但我将避免完全重复它们。

从理论上讲,如果您的其中一列被命名为不幸的名称"(system(paste0('kill ',Sys.getpid())))" (不要执行该名称,它会当场终止您的 R 会话!),您可能会遇到问题。这可能是一个足够的外部机会,不会因此而失眠,除非您打算将其放入 CRAN 上的包中。


更新:

对于下面评论中的特定情况,将表格分组然后sum应用于所有表格,.SDcols可能很有用。我知道确保该函数即使有dt一个命名列也能返回一致的结果的唯一方法var3是在函数环境内但在data.table环境外使用c().

set.seed(42)
dt <- data.table(
  x = rnorm(10),
  y = rnorm(10),
  z = sample(c("a","b","c"),size = 10, replace = TRUE)
)


test_func3 <- function(data, var, var2, var3) {
  ListOfColumns = c(var,var2)
  GroupColumn <- c(var3)
  dt[, lapply(.SD, sum), by= eval(GroupColumn), .SDcols = ListOfColumns]
}

test_func3(dt, 'x', 'y','z') 
Run Code Online (Sandbox Code Playgroud)

回报

   z         x         y
1: b 1.0531555  2.121852
2: a 0.3631284 -1.388861
3: c 4.0566838 -2.367558
Run Code Online (Sandbox Code Playgroud)