2014-01-09 45 views
11

假設我有一個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 
} 

我該怎麼辦?

+0

可能相關:http:///stackoverflow.com/q/16617226/324364 – joran

+0

和老一點:http://stackoverflow.com/a/12392269/1412059 – Roland

+0

...和1.6從data.table常見問題。 – joran

回答

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

+1更好,因爲它在單個分組中完成,而不是逐個添加每個列的分組。在這種情況下使用'.SD'不應該咬,因爲'.SD'裏面的所有數據都被使用了。 –

+0

@MattDowle作爲旁白 - 使用'.SD'的主要原因是當涉及'[.data.table'時,因爲該函數的開銷很大,所以'.SD [1:.N]'在「by」循環中慢了幾個數量級,即.SD' – eddi

+0

噢,是的,謝謝提醒。它在名單上以優化。 –

2

? data.table看看with

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 ] 

編輯:

,或者你只是改變colnames來回:

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) 

它與一起工作。

+0

這似乎不適用'by'。 – sds

+0

列重命名技巧很好,謝謝! – sds

7

這是類似的:

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

,但要將此與by=結合起來了,所以set()不夠靈活。這是一個故意的設計設計,在這方面不太可能改變。

我有時在回答結束時使用EVAL助手。
https://stackoverflow.com/a/20808573/403310
有些人在那種方式下,但我只是想像它構建一個動態的SQL語句,這是很常見的做法。 EVAL的方法提供了極大的靈活性,無需搔頭eval()quote()。要查看已構建的動態查詢(要檢查它),可以在EVAL幫助程序函數內添加print

但是,在這個簡單的例子,你可以用的:=的LHS和括號來告訴data.table查找值(清楚不過with=FALSE)和RHS需要get()

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

我不需要's'附近的人,是嗎? – sds

+0

是的,否則它會在循環的兩次迭代中創建一個名爲'「s」'的列。 –

+0

對不起 - 我錯過了'with = FALSE'。我們已經走向'(LHS):=',因爲這樣更容易閱讀。 'with = FALSE'不清楚它是指LHS還是':='的RHS。 –

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