从长数据框快速转换为宽数组

4

我有一个旧问题,由于数据集的大小而变得更具挑战性。这个问题是将数据框从长格式转换为宽矩阵:

set.seed(314)
A <- data.frame(field1 = sample(letters, 10, replace=FALSE), 
    field2 = sample(toupper(letters), 10, replace=FALSE), 
    value=1:10)

B <- with(A, tapply(value, list(field1, field2), sum))

这也可以使用旧的reshape在基础R中完成,或者更好地在plyr和reshape2中完成。 在plyr中:

daply(A, .(field1, field2), sum)

在reshape2中:

dcast(A, field1 ~ field2, sum)

问题在于我所拥有的数据框具有30+万行,其中至少有5000个唯一值的field1和20000个唯一值的field2。由于这样的大小,plyr会崩溃,reshape2偶尔会崩溃,tapply非常慢。机器不是限制因素(48GB,<50%利用率和8核Xeon)。对于此任务,最佳实践是什么?
注:此问题不是重复的。我明确指出输出应为宽数组。作为重复引用的答案引用了dcast.data.table的使用,它返回一个data.table。将data.table转换为数组是非常昂贵的操作。

你看过这个吗:https://dev59.com/D2Mk5IYBdhLWcg3w-Cmq 或者 https://dev59.com/imw05IYBdhLWcg3w_WxN? - thelatemail
希望我的“金锤”重复标记不是过早的... - mnel
+mnel,请查看上方的评论。这不是一个重复的问题。 - gappy
2个回答

3

以下是部分使用data.table的解决方案(仅用于聚合)。

编辑:我将dcast.data.table的答案替换为@BenBolker的答案,它完全避免了这一步骤,并且在内存和速度上都很高效-如果您正在寻找该解决方案,请检查修订版)。

创建一些具有相似规格的样本数据:

require(data.table) ## >= 1.9.2
set.seed(1L)
N = 30e6L
DT <- data.table(field1 = sample(paste0("F1_", 1:5000), N, TRUE), 
                 field2 = sample(paste0("F2_", 1:20000), N, TRUE),
                 value  = sample(10))

> tables()
#      NAME       NROW  MB COLS                KEY
# [1,] DT   30,000,000 574 field1,field2,value
# Total: 574MB

聚合:

system.time(ans <- DT[, list(value=sum(value)), by=list(field1, field2)])
#   user  system elapsed
# 15.097   3.357  18.454

编辑后的答案:然后你可以使用@BenBolker的(聪明的)解决方案,如下所示(完全避免了需要cast):

system.time({
    rlabs <- sort(unique(ans$field1))
    clabs <- sort(unique(ans$field2))
    fans <- matrix(NA,length(rlabs),length(clabs),
              dimnames=list(rlabs,clabs))
    fans[as.matrix(ans[,1:2, with=FALSE])] <- ans$value
})
#   user  system elapsed
# 18.630   1.524  20.154

谢谢,我会进行性能分析。目前我使用dcast.data.table,然后使用data.matrix(以及一些小的转换)。这应该会更快。 - gappy

2
我没有检查过速度,但这种低级方法是否更好?(使用适当的边距设置一个充满 NA 的矩阵,并使用 2 列矩阵索引进行填充...)
rlabs <- sort(unique(A$field1))
clabs <- sort(unique(A$field2))
B <- matrix(NA,length(rlabs),length(clabs),
      dimnames=list(rlabs,clabs))
B[as.matrix(A[,1:2])] <- A[,3]

如果您能将其设置为稀疏矩阵,那将非常好,但我假设您的value列中有零值...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接