从长数据帧到宽阵列的快速转换

时间:2014-05-16 02:24:33

标签: r plyr reshape2

我有一个老问题,因为数据集的大小而具有挑战性。问题是将数据帧从长矩阵转换为宽矩阵:

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))

这也可以通过基础R中的旧重塑来完成,或者更好地在plyr和reshape2中完成。在普利尔:

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

在reshape2中:

dcast(A, field1 ~ field2, sum)

问题是我的数据帧有30 + m行,field1至少有5000个唯一值,field2有20000个唯一值。有了这个尺寸,plyr崩溃,重塑2偶尔崩溃,并且tapply非常慢。该机器不是约束(48GB,<50%利用率和8核Xeon)。这项任务的最佳实践是什么?

N.B。:这个问题重复。我明确提到输出应该是一个宽数组。作为重复引用的答案引用dcast.data.table的使用,它返回data.table。将data.table转换为数组是一项非常昂贵的操作。

2 个答案:

答案 0 :(得分:3)

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

编辑:我用@BenBolker的答案替换了dcast.data.table答案,因为它完全避免了这一步骤,并且内存和速度都很高效 - 请检查修订版本是否为&#39 ;重新寻找解决方案)。

创建一些样本数据(具有类似的规范):

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

答案 1 :(得分:2)

我没有检查速度,但是这种低级方法更好吗? (设置一个带有适当边距的NA s的矩阵,并使用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列中的值为零...