为什么unsplit这么慢

时间:2014-02-16 12:46:38

标签: r

我有大约450 000行的以下数据:

'data.frame':   451712 obs. of  7 variables:
 $ mid: int  5732 5732 5732 5732 5732 5732 5732 5732 5732 5732 ...
 $ id : int  25 26 28 29 30 31 33 36 37 38 ...
 $ x  : num  3197 5545 3205 6947 7264 ...
 $ y  : num  6138 5555 6209 5465 5230 ...
 $ t  : Factor w/ 2 levels "C","L": 2 2 2 2 2 2 2 2 2 2 ...
 $ r  : Factor w/ 5 levels "Aberrant","Both",..: 3 1 3 4 4 4 4 4 4 4 ...
 $ c  : num  1 0 1 2 2 2 2 3 2 2 ...

为什么以下操作需要很长时间(> 5分钟,我停止执行)

 unsplit(split(data, data$mid), data$mid)

split本身很快,但在合并数据时,它需要永远。如果我将数据截断为100 000行,那么我可以在10秒内得到结果,但随着行数的增加,时间不会增加。

1 个答案:

答案 0 :(得分:7)

虽然看起来确实很奇怪,但您需要了解数据框的一件事是它们的速度非常慢。修改数据帧的每个操作都很昂贵,unsplit正在做的是通过重新插入来修改每个拆分的数据帧。关于内部逻辑的一些要求每次修改时都要复制整个数据帧。这被称为unsplit的一部分:

`split<-.data.frame`
function (x, f, drop = FALSE, ..., value) 
{
    ix <- split(seq_len(nrow(x)), f, drop = drop, ...)
    n <- length(value)
    j <- 0
    for (i in ix) {
        j <- j%%n + 1
        x[i, ] <- value[[j]]
    }
    x
}
<bytecode: 0x7ffd5e282c68>
<environment: namespace:base>

x这里是一个初始化为结果大小的data.frame,你可以看到我们基本上每次拆分都会插入它。由于您的数据框架很大,因此每个插入都非常昂贵。为了强调这可怕的原因,请考虑以下示例,其中我们基准覆盖数据帧中的列中的每个值,而不是使用等效矩阵执行完全相同的操作。即使我们做的完全相同,数据帧版本也要慢30倍!这是一个微小的数据框架。当data.frame很大时,您可以快速了解这是怎么回事。

df <- data.frame(a=seq(1:100), b=runif(100), c=sample(1:10, 100, rep=T))
mx <- as.matrix(df)

microbenchmark(for(i in 1:nrow(df)) df[i, 2] <- 1)
# Unit: milliseconds
#                                 expr      min       lq   median       uq      max neval
#  for (i in 1:nrow(df)) df[i, 2] <- 1 4.018833 4.273562 4.584293 4.726672 23.46349   100    

microbenchmark(for(i in 1:nrow(mx)) mx[i, 2] <- 1)
# Unit: microseconds
#                                 expr     min       lq   median       uq     max neval
#  for (i in 1:nrow(mx)) mx[i, 2] <- 1 148.304 153.9795 158.5975 163.7065 277.861   100

顺便说一下,这就是为什么对于上述类型的操作,建议首先计算向量,最后将向量替换为数据帧列,以便只修改数据帧一次。

回复:使用data.tabledplyr,我强烈要求您将split / unsplit所做的分析类型作为问题发布,我怀疑你我们会惊讶于这些软件包在解决您的问题方面的强大程度。