[英]Proper/fastest way to reshape a data.table
我在R中有一個數據表 :
library(data.table)
set.seed(1234)
DT <- data.table(x=rep(c(1,2,3),each=4), y=c("A","B"), v=sample(1:100,12))
DT
x y v
[1,] 1 A 12
[2,] 1 B 62
[3,] 1 A 60
[4,] 1 B 61
[5,] 2 A 83
[6,] 2 B 97
[7,] 2 A 1
[8,] 2 B 22
[9,] 3 A 99
[10,] 3 B 47
[11,] 3 A 63
[12,] 3 B 49
我可以通過data.table中的組輕松地對變量v求和:
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
out
x y SUM
[1,] 1 A 72
[2,] 1 B 123
[3,] 2 A 84
[4,] 2 B 119
[5,] 3 A 162
[6,] 3 B 96
但是,我想將組(y)作為列而不是行。 我可以使用reshape
完成此操作:
out <- reshape(out,direction='wide',idvar='x', timevar='y')
out
x SUM.A SUM.B
[1,] 1 72 123
[2,] 2 84 119
[3,] 3 162 96
匯總數據后,是否有更有效的方式來重塑數據? 是否有任何方法可以使用data.table操作將這些操作組合為一個步驟?
data.table
包實現了更快的melt/dcast
功能(用C語言melt/dcast
)。 通過允許熔化和澆鑄多列 ,它還具有其他功能。 請在Github上使用data.tables查看新的高效重塑 。
從v1.9.0版本開始提供data.table的melt / dcast功能,其功能包括:
鑄造前無需加載reshape2
包裝。 但是,如果您希望將其加載以進行其他操作,請在加載data.table
之前先加載data.table
。
dcast
也是S3的泛型。 沒有更多的dcast.data.table()
。 只需使用dcast()
。
melt
能夠融化“列表”類型的列。
獲得variable.factor
和value.factor
,默認情況下分別為TRUE
和FALSE
,以與reshape2
兼容。 這允許直接控制variable
和value
列的輸出類型(是否為因子)。
melt.data.table
的na.rm = TRUE
參數進行內部優化,以在融化過程中直接去除NA,因此效率更高。
新增內容: melt
可以接受一個measure.vars
列表measure.vars
列表中每個元素中指定的measure.vars
和列將合並在一起。 通過使用patterns()
可以進一步簡化此過程。 參見暈影或?melt
。
dcast
:
接受多個fun.aggregate
和多個value.var
。 參見小插圖或?dcast
。
直接在公式中使用rowid()
函數生成一個id列,有時需要該ID來唯一標識行。 參見?dcast。
舊基准:
melt
:1000萬行和5列,從61.3秒減少到1.2秒。 dcast
:1百萬行4列,從192秒減少到3.6秒。 《科隆提醒(2013年12月)》演示幻燈片32: 為什么不提交dcast
pull請求來reshape2
?
我剛剛在SO上看到了來自Arun的大量代碼。 所以我想有一個data.table
解決方案。 應用於此問題:
library(data.table)
set.seed(1234)
DT <- data.table(x=rep(c(1,2,3),each=1e6),
y=c("A","B"),
v=sample(1:100,12))
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
# edit (mnel) to avoid setNames which creates a copy
# when calling `names<-` inside the function
out[, as.list(setattr(SUM, 'names', y)), by=list(x)]
})
x A B
1: 1 26499966 28166677
2: 2 26499978 28166673
3: 3 26500056 28166650
這與DWin的方法具有相同的結果:
tapply(DT$v,list(DT$x, DT$y), FUN=sum)
A B
1 26499966 28166677
2 26499978 28166673
3 26500056 28166650
而且,它很快:
system.time({
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
out[, as.list(setattr(SUM, 'names', y)), by=list(x)]})
## user system elapsed
## 0.64 0.05 0.70
system.time(tapply(DT$v,list(DT$x, DT$y), FUN=sum))
## user system elapsed
## 7.23 0.16 7.39
更新
為了使該解決方案也適用於非平衡數據集(即某些組合不存在),您必須首先在數據表中輸入這些組合:
library(data.table)
set.seed(1234)
DT <- data.table(x=c(rep(c(1,2,3),each=4),3,4), y=c("A","B"), v=sample(1:100,14))
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
setkey(out, x, y)
intDT <- expand.grid(unique(out[,x]), unique(out[,y]))
setnames(intDT, c("x", "y"))
out <- out[intDT]
out[, as.list(setattr(SUM, 'names', y)), by=list(x)]
摘要
結合上面的評論,這是一線解決方案:
DT[, sum(v), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][,
setNames(as.list(V1), paste(y)), by = x]
也可以很容易地修改它,使其不僅具有總和,例如:
DT[, list(sum(v), mean(v)), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][,
setNames(as.list(c(V1, V2)), c(paste0(y,".sum"), paste0(y,".mean"))), by = x]
# x A.sum B.sum A.mean B.mean
#1: 1 72 123 36.00000 61.5
#2: 2 84 119 42.00000 59.5
#3: 3 187 96 62.33333 48.0
#4: 4 NA 81 NA 81.0
Data.table對象繼承自“ data.frame”,因此您可以使用tapply:
> tapply(DT$v,list(DT$x, DT$y), FUN=sum)
AA BB
a 72 123
b 84 119
c 162 96
您可以從reshape2
庫使用dcast
。 這是代碼
# DUMMY DATA
library(data.table)
mydf = data.table(
x = rep(1:3, each = 4),
y = rep(c('A', 'B'), times = 2),
v = rpois(12, 30)
)
# USE RESHAPE2
library(reshape2)
dcast(mydf, x ~ y, fun = sum, value_var = "v")
注意: tapply
解決方案將更快。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.