2015-08-28 78 views
18

我不是很清楚使用.SDby在data.table中使用lapply.SD R

例如,下面的代碼段是否意味着:'將DT中的所有列更改爲因子,除了AB?'它還在data.table手冊中說:「.SD是指data.table的每個組的子集(不包括分組列)」 - 所以列AB被排除在外?

DT = DT[ ,lapply(.SD, as.factor), by=.(A,B)] 

不過,我也讀到by意味着像「按組」中的SQL當你聚集。例如,如果我想總結(如SQL中的colsum)除AB之外的所有列,我仍然使用類似的東西嗎?或者在這種情況下,下面的代碼是否意味着按列AB中的值進行求和和分組? (採取由A,B和與集團作爲SQL)

DT[,lapply(.SD,sum),by=.(A,B)] 

那麼我該怎麼做一個簡單的colsum在所有列,除了AB

+0

'DT [,colSums(.SD),. SDcols = C(A, B)]'或'DT [,lapply(.SD,sum),。SDcols = -c(A,B)]' – Khashaa

+0

使用'by'意味着在每個'A'x'B'配對中, 'DT'中每個_other_列的值。 @ Khashaa的評論是(一些方法)如何總結所有列,除了'A'和'B',_not by group_ – MichaelChirico

+0

@ MichaelChirico,當我改變列類型時,雖然如第一個例子中那樣,'by'表示排除我猜對吧?哪一個更快? 'colSums'或'lapply'? – KTY

回答

35

只是爲了說明上面的示例中的意見,讓我們

set.seed(10238) 
# A and B are the "id" variables within which the 
# "data" variables C and D vary meaningfully 
DT = data.table(A = rep(1:3, each = 5), B = rep(1:5, 3), 
       C = sample(15), D = sample(15)) 
DT 
#  A B C D 
# 1: 1 1 14 11 
# 2: 1 2 3 8 
# 3: 1 3 15 1 
# 4: 1 4 1 14 
# 5: 1 5 5 9 
# 6: 2 1 7 13 
# 7: 2 2 2 12 
# 8: 2 3 8 6 
# 9: 2 4 9 15 
# 10: 2 5 4 3 
# 11: 3 1 6 5 
# 12: 3 2 12 10 
# 13: 3 3 10 4 
# 14: 3 4 13 7 
# 15: 3 5 11 2 

比較如下:

#Sum all columns 
DT[ , lapply(.SD, sum)] 
#  A B C D 
# 1: 30 45 120 120 

#Sum all columns EXCEPT A, grouping BY A 
DT[ , lapply(.SD, sum), by = A] 
# A B C D 
# 1: 1 15 38 43 
# 2: 2 15 30 49 
# 3: 3 15 52 28 

#Sum all columns EXCEPT A 
DT[ , lapply(.SD, sum), .SDcols = !"A"] 
#  B C D 
# 1: 45 120 120 

#Sum all columns EXCEPT A, grouping BY B 
DT[ , lapply(.SD, sum), by = B, .SDcols = !"A"] 
# B C D 
# 1: 1 27 29 
# 2: 2 17 30 
# 3: 3 33 11 
# 4: 4 23 36 
# 5: 5 20 14 

的幾個注意事項:

  • 你說「不以下snippet ...更改DT中的所有列...「

答案是沒有,這對data.table非常重要。返回的對象是新的data.table,並且DT中的所有列都與運行代碼之前的完全相同。

  • 您提到想要更改列類型

參考點之上再次,注意你的代碼(DT[ , lapply(.SD, as.factor)])返回一個data.table並不會改變DT可言。一種方法(不正確)的方式是使用中的data.frame s完成,它將覆蓋舊的data.table和您返回的新data.table,即DT = DT[ , lapply(.SD, as.factor)]

這是浪費的,因爲它涉及創建DT的副本,當DT很大時,這可能是效率的殺手。解決此問題的正確data.table方法是使用`:=`(例如DT[ , names(DT) := lapply(.SD, as.factor)])通過引用來更新列,從而不會創建數據的副本。有關詳細信息,請參閱data.table's reference semantics vignette

  • 您提到比較效率lapply(.SD, sum)colSums的效率。 sumdata.table內部進行了優化(您可以注意到,在[]內添加verbose = TRUE參數的輸出是真實的);看到這個動作,讓牛肉你DT了一下,運行的基準:

結果:

library(data.table) 
set.seed(12039) 
nn = 1e7; kk = seq(100L) 
DT = as.data.table(replicate(26, sample(kk, nn, T))) 
DT[ , LETTERS[1:2] := .(sample(100, nn, T), sample(100, nn, T))] 

library(microbenchmark) 
microbenchmark(times = 100L, 
       colsums = colSums(DT[ , !c("A", "B"), with = FALSE]), 
       lapplys = DT[ , lapply(.SD, sum), .SDcols = !c("A", "B")]) 
# Unit: milliseconds 
#  expr  min  lq  mean median  uq  max neval 
# colsums 848.9310 886.6289 906.8105 896.7696 925.4353 997.0001 100 
# lapplys 144.5028 145.7165 154.4077 147.5586 153.2286 253.6726 100 
+2

帶有'set'的'for'循環是轉換大量列的另一種選擇,在此答案底部提及並由Arun和Matt建議/認可:http://stackoverflow.com/a/16846530/1191259 – Frank

+0

@Frank是的,這就是我現在要做的事情,但是花了我相當長的時間才把頭髮纏繞在那裏。但能夠早日接觸到這一點真是太好了。 – MichaelChirico

+0

@MichaelChirico,感謝您在不創建新DT的情況下更改列類型的提示!很有幫助。 – KTY