Zac*_*ach 66 r 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
Run Code Online (Sandbox Code Playgroud)
我可以通过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
Run Code Online (Sandbox Code Playgroud)
但是,我想将组(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
Run Code Online (Sandbox Code Playgroud)
在汇总数据后,是否有更有效的方法来重塑数据?有没有办法使用data.table操作将这些操作合并为一个步骤?
Zac*_*ach 73
该data.table包实现了更快的melt/dcast功能(在C中).它还具有额外的功能,允许熔化和浇铸多个色谱柱.请在Github上使用data.tables查看新的Efficient重塑.
data.table的融合/ dcast函数自v1.9.0开始提供,其功能包括:
在reshape2铸造之前不需要加载包装.但如果您希望将其加载到其他操作中,请在加载前加载它data.table.
dcast也是S3通用的.没有了dcast.data.table().只是用dcast().
melt:
能够在'list'类型的列上熔化.
增益variable.factor,value.factor默认情况下TRUE,FALSE分别与兼容性相关reshape2.这允许直接控制输出类型variable和value列(作为因素与否).
melt.data.table该na.rm = TRUE参数在内部进行了优化,可在熔化过程中直接去除NA,因此效率更高.
新:melt可以接受列表,列表的measure.vars每个元素中指定的列将组合在一起.这通过使用进一步促进patterns().看小插图或?melt.
dcast:
接受多个fun.aggregate和多个 value.var.看小插图或?dcast.
rowid()直接在公式中使用函数来生成id列,有时需要唯一地标识行.见?dcast.
旧基准:
melt :1000万行和5列,61.3秒减少到1.2秒. dcast :100万行4列,192秒减少到3.6秒. 提醒科隆(2013年12月)演示幻灯片32:为什么不提交dcast拉动请求reshape2?
Chr*_*h_J 32
我刚看到这个代码块很大,从阿伦这里SO.所以我猜有一个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
Run Code Online (Sandbox Code Playgroud)
这给出了与DWin方法相同的结果:
tapply(DT$v,list(DT$x, DT$y), FUN=sum)
A B
1 26499966 28166677
2 26499978 28166673
3 26500056 28166650
Run Code Online (Sandbox Code Playgroud)
它也很快:
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
Run Code Online (Sandbox Code Playgroud)
UPDATE
因此,此解决方案也适用于非平衡数据集(即某些组合不存在),您必须首先在数据表中输入这些数据集:
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)]
Run Code Online (Sandbox Code Playgroud)
摘要
将评论与上述结合起来,这是一线解决方案:
DT[, sum(v), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][,
setNames(as.list(V1), paste(y)), by = x]
Run Code Online (Sandbox Code Playgroud)
修改它也很容易,不仅仅是总和,例如:
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
Run Code Online (Sandbox Code Playgroud)
42-*_*42- 21
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
Run Code Online (Sandbox Code Playgroud)
您可以dcast在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")
Run Code Online (Sandbox Code Playgroud)
注意:tapply解决方案会更快.