当变量名存储在字符向量中时,选择/分配给data.table

fra*_*nkc 78 r data.table

data.table如果变量名存储在字符向量中,如何引用变量?例如,这适用于data.frame:

df <- data.frame(col1 = 1:3)
colname <- "col1"
df[colname] <- 4:6
df
#   col1
# 1    4
# 2    5
# 3    6
Run Code Online (Sandbox Code Playgroud)

如何使用或不使用:=表示法对data.table执行相同的操作?显而易见的事情是dt[ , list(colname)]行不通的(我也没想到).

Mat*_*wle 114

以编程方式选择变量的两种方法:

  1. with = FALSE:

    DT = data.table(col1 = 1:3)
    colname = "col1"
    DT[, colname, with = FALSE] 
    #    col1
    # 1:    1
    # 2:    2
    # 3:    3
    
    Run Code Online (Sandbox Code Playgroud)
  2. 'dot dot'(..)前缀:

    DT[, ..colname]    
    #    col1
    # 1:    1
    # 2:    2
    # 3:    3
    
    Run Code Online (Sandbox Code Playgroud)

有关"点点"(..)表示法的进一步说明,请参阅1.10.2中的新功能(目前未在帮助文本中对其进行描述).

分配给变量,请将:=括号内的LHS包装起来:

DT[, (colname) := 4:6]    
#    col1
# 1:    4
# 2:    5
# 3:    6
Run Code Online (Sandbox Code Playgroud)

后者称为列plonk,因为您通过引用替换整个列向量.如果存在子集i,则它将通过引用进行子分配.parens周围(colname)是CRAN 2014年10月版v1.9.4中引入的简写.这是新闻项目:

在所有情况下,使用with = FALSEwith :=现在已被弃用,因为:=在一段时间内首选包含括号的LHS .

colVar = "col1"
DT[, colVar := 1, with = FALSE]                 # deprecated, still works silently
DT[, (colVar) := 1]                             # please change to this
DT[, c("col1", "col2") := 1]                    # no change
DT[, 2:4 := 1]                                  # no change
DT[, c("col1","col2") := list(sum(a), mean(b)]  # no change
DT[, `:=`(...), by = ...]                       # no change
Run Code Online (Sandbox Code Playgroud)

另见详细信息部分?`:=`:

DT[i, (colnamevector) := value]
# [...] The parens are enough to stop the LHS being a symbol
Run Code Online (Sandbox Code Playgroud)

并且在评论中回答进一步的问题,这是一种方式(像往常一样有很多方法):

DT[, colname := cumsum(get(colname)), with = FALSE]
#    col1
# 1:    4
# 2:    9
# 3:   15 
Run Code Online (Sandbox Code Playgroud)

或者,你可能会发现更容易阅读,编写和调试只是eval一个paste类似于构建一个动态的SQL语句发送到服务器:

expr = paste0("DT[,",colname,":=cumsum(",colname,")]")
expr
# [1] "DT[,col1:=cumsum(col1)]"

eval(parse(text=expr))
#    col1
# 1:    4
# 2:   13
# 3:   28
Run Code Online (Sandbox Code Playgroud)

如果你这么做,你可以定义一个辅助函数EVAL:

EVAL = function(...)eval(parse(text=paste0(...)),envir=parent.frame(2))

EVAL("DT[,",colname,":=cumsum(",colname,")]")
#    col1
# 1:    4
# 2:   17
# 3:   45
Run Code Online (Sandbox Code Playgroud)

现在data.table1.8.2自动优化j效率,最好使用该eval方法.例如,get()in j可以防止一些优化.

或者,有set().一个低开销,功能性的形式:=,这里很好.见?set.

set(DT, j = colname, value = cumsum(DT[[colname]]))
DT
#    col1
# 1:    4
# 2:   21
# 3:   66
Run Code Online (Sandbox Code Playgroud)

  • 请注意,您可以使用gsubfn包中的`fn $`的准perl类型字符串插值来提高EVAL解决方案的可读性:`library(gsubfn); fn $ EVAL("DT [,$ colname:= cumsum($ colname)]")`. (2认同)

小智 8

*这不是一个真正的答案,但我没有足够的街头信息来发表评论:/

无论如何,对于那些可能希望在数据表中实际创建一个名称存储在变量中的新列的人来说,我已经完成了以下工作.我不知道它的表现.有任何改进建议吗?假设一个无名的新列将始终被赋予名称V1是否安全?

colname <- as.name("users")
# Google Analytics query is run with chosen metric and resulting data is assigned to DT
DT2 <- DT[, sum(eval(colname, .SD)), by = country]
setnames(DT2, "V1", as.character(colname))
Run Code Online (Sandbox Code Playgroud)

请注意,我可以在sum()中引用它,但似乎无法在同一步骤中分配它.顺便说一句,我需要这样做的原因是colname将基于Shiny应用程序中的用户输入.

  • 假设`V1`是新名称是不安全的.例如,如果您使用`fread`读取csv并且有一个未命名的列,它将具有`V1'名称(并且`read.csv`将给出'X`).所以你的桌子可能已经有了'V1`.也许只是通过`names(DT)[length(names(DT))]来获得名称 (2认同)

C.K*_*C.K 5

通过变量或函数从 data.table 中检索多列:

library(data.table)

x <- data.table(this=1:2,that=1:2,whatever=1:2)

# === explicit call
x[, .(that, whatever)]
x[, c('that', 'whatever')]

# === indirect via  variable
# ... direct assignment
mycols <- c('that','whatever')
# ... same as result of a function call
mycols <- grep('a', colnames(x), value=TRUE)

x[, ..mycols]
x[, .SD, .SDcols=mycols]

# === direct 1-liner usage
x[, .SD, .SDcols=c('that','whatever')]
x[, .SD, .SDcols=grep('a', colnames(x), value=TRUE)]
Run Code Online (Sandbox Code Playgroud)

全部产生

   that whatever
1:    1        1
2:    2        2
Run Code Online (Sandbox Code Playgroud)

我发现这种.SDcols方式最优雅。