98

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

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

如何对 data.table 执行相同的操作,无论是否使用:=符号?显而易见的事情dt[ , list(colname)]不起作用(我也没想到)。

4

6 回答 6

138

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

  1. with = FALSE

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

     DT[, ..colname]    
     #    col1
     # 1:    1
     # 2:    2
     # 3:    3
    

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

分配给变量,请将 LHS:=括在括号中:

DT[, (colname) := 4:6]    
#    col1
# 1:    4
# 2:    5
# 3:    6

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

现在在所有情况下都不推荐使用with = FALSEwith ,因为一段时间以来一直首选用括号包裹 LHS 。:=:=

colVar = "col1"
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

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

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

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

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

eval或者,您可能会发现仅对a进行读取、写入和调试更容易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

如果你经常这样做,你可以定义一个辅助函数EVAL

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

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

现在data.table1.8.2 自动优化j效率,使用该eval方法可能更可取。例如,get()inj阻止了一些优化。

或者,有set()。的低开销、函数形式:=,在这里会很好。见?set

set(DT, j = colname, value = cumsum(DT[[colname]]))
DT
#    col1
# 1:    4
# 2:   21
# 3:   66
于 2012-09-12T16:05:27.197 回答
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))

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

于 2015-06-30T16:26:42.943 回答
4

通过变量或函数从 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)]

这都产生

   that whatever
1:    1        1
2:    2        2

我找到了.SDcols最优雅的方式。

于 2020-05-05T08:33:30.833 回答
2

对于多列和应用于列值的函数。

从函数更新值时,RHS 必须是列表对象,因此使用.SDwith循环就lapply可以了。

下面的示例将整数列转换为数字列

a1 <- data.table(a=1:5, b=6:10, c1=letters[1:5])
sapply(a1, class)  # show classes of columns
#         a           b          c1 
# "integer"   "integer" "character" 

# column name character vector
nm <- c("a", "b")

# Convert columns a and b to numeric type
a1[, j = (nm) := lapply(.SD, as.numeric ), .SDcols = nm ]

sapply(a1, class)
#         a           b          c1 
# "numeric"   "numeric" "character" 
于 2017-04-17T04:04:38.663 回答
2

你可以试试这个:

colname <- as.name("COL_NAME")
DT2 <- DT[, list(COL_SUM=sum(eval(colname, .SD))), by = c(group)]
于 2020-03-19T04:13:30.137 回答
1

在开发版本 1.14.3 中,获得了一个用于对 data.table 进行编程的新接口,请参见New Features中的第 10 项。它使用新env =参数。

library(data.table) # development version 1.14.3 used
dt <- data.table(col1 = 1:3)
colname <- "col1"

dt[, cn := cn + 3L, env = list(cn = colname)][]
    col1
   <int>
1:     4
2:     5
3:     6
于 2021-12-09T08:34:14.833 回答