正确/最快的方法来重塑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 xyv [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中的组对variablesv进行求和:
out <- DT[,list(SUM=sum(v)),by=list(x,y)] out xy 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
function(C语言)。 它还具有附加function,允许熔化和浇铸多个色谱柱 。 请参阅使用 Github上的data.tables进行新的Efficient重塑 。
data.table的融合/播放函数自v1.9.0开始可用,其function包括:
-
在铸造之前不需要加载
reshape2
包装。 但是如果你想把它加载到其他操作上,请在加载data.table
之前加载data.table
。 -
dcast
也是S3通用的。 没有更多的dcast.data.table()
。 只要使用dcast()
。 -
melt
:-
能够在“列表”types的列上融化。
-
获得
variable.factor
和value.factor
,默认情况下为TRUE
和FALSE
以便与reshape2
兼容。 这允许直接控制variable
和value
列的输出types(作为因素或不)。 -
melt.data.table
的na.rm = TRUE
参数在内部进行了优化,以便在熔化期间直接去除na.rm = TRUE
,因此效率更高。 -
新:
melt
可以接受measure.vars
的列表,列表的每个元素中指定的列将被组合在一起。 这是通过使用patterns()
。 看小插曲或?melt
。
-
-
dcast
:-
接受多个
fun.aggregate
和多个value.var
。 见小插曲或?dcast
。 -
直接在公式中使用
rowid()
函数来生成一个id列,这有时需要唯一标识行。 请参阅播报。
-
-
老基准:
-
melt
:1000万行5列,61.3秒减less到1.2秒。 -
dcast
:100万行4列,192秒减less到3.6秒。
-
科隆的提醒(2013年12月)演示幻灯片32: 为什么不提交dcast
pull请求到reshape2
?
这个特性现在被实现在data.table中(从版本1.8.11开始),在Zach上面的答案中可以看到。
我刚刚在这里看到了来自Arun的这一大块代码。 所以我想有一个data.table
解决scheme。 应用于这个问题:
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 AB 1: 1 26499966 28166677 2: 2 26499978 28166673 3: 3 26500056 28166650
这与德文的方法给出了相同的结果:
tapply(DT$v,list(DT$x, DT$y), FUN=sum) AB 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
UPDATE
因此,这个解决scheme也适用于非平衡数据集(即某些组合不存在),您必须首先在数据表中input这些数据:
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)]
概要
结合上面的评论,这里是单线解决scheme:
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对象inheritance自“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
库中的reshape2
。 这是代码
# 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
解决scheme会更快。