按列名称的data.table操作

时间:2014-01-09 15:22:00

标签: r data.table

假设我有data.table

a <- data.table(id=c(1,1,2,2,3),a=21:25,b=11:15,key="id")

我可以像这样添加新列:

a[, sa := sum(a), by="id"]
a[, sb := sum(b), by="id"]
> a
   id  a  b sa sb
1:  1 21 11 43 23
2:  1 22 12 43 23
3:  2 23 13 47 27
4:  2 24 14 47 27
5:  3 25 15 25 15

但是,假设我有列名

for (n in c("a","b")) {
  s <- paste0("s",n)
  a[, s := sum(n), by="id", with=FALSE] # ERROR: invalid 'type' (character) of argument
}

我该怎么办?

4 个答案:

答案 0 :(得分:10)

你也可以这样做:

a <- data.table(id=c(1,1,2,2,3),a=21:25,b=11:15,key="id")

a[, c("sa", "sb") := lapply(.SD, sum), by = id]

或稍微更普遍:

cols.to.sum = c("a", "b")
a[, paste0("s", cols.to.sum) := lapply(.SD, sum), by = id, .SDcols = cols.to.sum]

答案 1 :(得分:7)

这类似于:

How to generate a linear combination of variables and update table using data.table in a loop call?

但您也希望将其与by=结合使用,因此set()不够灵活。这是一个刻意的设计设计,set()在这方面不太可能改变。

我有时会在答案结尾处使用EVAL助手 https://stackoverflow.com/a/20808573/403310有些人对这种方法感到畏缩,但我认为这就像构建动态SQL语句一样,这是很常见的做法。 EVAL方法提供了极大的灵活性,而不会让人头疼eval()quote()。要查看已构建的动态查询(检查它),您可以在print辅助函数中添加EVAL

但是,在这个简单的示例中,您可以使用括号将:=的LHS包装起来告诉data.table查找值(比with=FALSE更清晰),并且RHS需要get() 1}}。

for (n in c("a","b")) {
  s <- paste0("s",n)
  a[, (s) := sum(get(n)), by="id"]
}

答案 2 :(得分:2)

查看with中的? data.table

dt <- data.table(id=1:5,a=21:25,b=11:15,key="id")
dt[, n3 := dt[ , n1, with = FALSE ] * dt[ , n2, with = FALSE ], with = FALSE ]

编辑:

或者你只是前后改变了这些名字:

dt <- data.table(id=1:5,a=21:25,b=11:15,key="id")
dt[ , dt.names["n3"] := 1L, with = FALSE ]

dt.names <- c( n1 = "a", n2 = "b", n3 = "c" )
setnames( dt, dt.names, names(dt.names) )

dt[ , n3 := n1 * n2, by = "id" ]
setnames( dt, names(dt.names), dt.names )

与。一起工作。

答案 3 :(得分:0)

这是一种执行调用重整并避免.SD

的任何开销的方法
# a helper function
makeCall <- function(x,fun) bquote(.(fun)(.(x)))
# the columns you wish to sum (apply function to)
cols <- c('a','b')
new.cols <- paste0('s',cols)
# create named list of names
name.cols <- setNames(sapply(cols,as.name), new.cols)
# create the call
my_call <-  as.call(c(as.name(':='), lapply(name.cols, makeCall, fun = as.name('sum'))))
(a[, eval(my_call), by = 'id'])

#    id  a  b sa sb
# 1:  1 21 11 43 23
# 2:  1 22 12 43 23
# 3:  2 23 13 47 27
# 4:  2 24 14 47 27
# 5:  3 25 15 25 15