data.table 赋值中评估字符串的快捷方式

Shortcut for evaluating strings in data.table assignments

data.table 争论数据,我发现自己写了很多像第三行这样的行:

DT = data.table(a = 1:10)
name = 'a'
DT[,eval(parse(text=sprintf('%s_plus_one := %s + 1',name,name)))]

我希望减少到

DT[,s('%s_plus_one := %s + 1',name,name)]

使用如下函数:

# s is *very* short for substitute and evalute
s <- function(...)
    eval(parse(text=sprintf(...)))

但是我得到这个错误:

> DT[,s('%s_plus_one := %s + 1',name,name)]
    Check that is.data.table(DT) == TRUE. Otherwise, := and `:=`(...) are defined for use in j, once only and in particular ways. See help(":=").

我知道我可以做到:

# sp is short for substitute and parse
sp <- function(...)
    parse(text=sprintf(...))

DT[,eval(sp('%s_plus_one := %s + 1',name,name))]
DT
#>      a a_plus_one
#>  1:  1          2
#>  2:  2          3
#>  3:  3          4
#>  4:  4          5

但是在 data.table 赋值中构建要计算的字符串是如此常见,我希望尽可能减少输入。

很多时候甚至不需要进入 eval(parse()) 的世界。即,对于此示例,将名称放在左侧的括号中,并在右侧使用 get

DT[, (paste0(name,"_plus_one")) := get(name) + 1]

DT
#     a a_plus_one
# 1:  1          2
# 2:  2          3
# 3:  3          4
# 4:  4          5
# 5:  5          6
# 6:  6          7
# 7:  7          8
# 8:  8          9
# 9:  9         10
#10: 10         11

我们可以使用.SDcols

DT[,(paste0(name,"_plus_one")) := .SD+1, .SDcols=name]
DT
#     a a_plus_one
# 1:  1          2
# 2:  2          3
# 3:  3          4
# 4:  4          5
# 5:  5          6
# 6:  6          7
# 7:  7          8
# 8:  8          9
# 9:  9         10
#10: 10         11

正如@thelatemail 提到的,这也适用于多列

DT = data.table(a = 1:10, b=2:11)
name <- c("a","b")
DT[,(paste0(name,"_plus_one")) := .SD + 1, .SDcols=name]