R中的高效数据帧迭代

时间:2014-07-24 16:17:42

标签: r

假设我有一个500万行数据帧,有两列,为了简单起见,这个数据帧只有十行:

df <- data.frame(start=c(11,21,31,41,42,54,61,63), end=c(20,30,40,50,51,63,70,72))

我希望能够在数字向量中生成以下数字:

11 to 20, 21 to 30, 31 to 40, 41 to 50, 51, 54-63, 64-70, 71-72

然后取新矢量的长度(在这种情况下,10 + 10 + 10 + 10 + 1 + 10 + 7 + 2)= 60

*注意,我不需要矢量本身,只需它的长度即可。因此,如果某人有更智能的逻辑方法来获得长度,那就很受欢迎了。

基本上,完成的是数据帧中的每一行,从开始到结束的序列,以及所有这些序列的组合,然后过滤UNIQUE值。

所以我使用了一种方法:

length(unique(c(apply(df, 1, function(x) {
    return(as.numeric(x[1]):as.numeric(x[2]))
}))))

在我的五百万行数据框中证明非常慢。

更快更有效的解决方案?奖金,请尝试添加系统时间。

用户系统已用完  19.946 0.620 20.477

2 个答案:

答案 0 :(得分:2)

这应该有效,假设您的数据已经过排序。

library(dplyr)  # for the lag function

with(df, sum(end - pmax(start, lag(end, 1, default = 0)+1) + 1))
#[1] 60

library(microbenchmark)
microbenchmark(
  beginneR={with(df, sum(end - pmax(start, lag(end, 1, default = 0)+1) + 1))},
  r2evans={vec <- pmax(mm[,1], c(0,1+head(mm[,2],n=-1))); sum(mm[,2]-vec+1);},
  times = 1000
)

Unit: microseconds
     expr     min       lq  median       uq       max neval
beginneR   37.398  41.4455  42.731  44.0795    74.349  1000
r2evans    31.788  35.2470  36.827  38.3925  9298.669  1000

因此矩阵仍然更快,但不多(并且此处仍未包含转换步骤)。我想知道为什么@ r2evans的答案中的最大持续时间与所有其他值(实际上很快)相比如此之高

答案 1 :(得分:2)

另一种方法:

mm <- as.matrix(df) ## critical for performance/scalability
(vec <- pmax(mm[,1], c(0,1+head(mm[,2],n=-1))))
##  [1] 11 21 31 41 51 54 64 71
sum(mm[,2] - vec + 1)
##  [1] 60

(这应该很好地扩展,当然比data.frames更好。)

修改:在我更新代码以使用矩阵而没有apply调用之后,我对其实现进行了快速基准测试,与其他答案相比(这也是正确的):< / p>

library(microbenchmark)
library(dplyr)
microbenchmark(
    beginneR={
        df <- data.frame(start=c(11,21,31,41,42,54,61,63),
                         end=c(20,30,40,50,51,63,70,72))
        with(df, sum(end - pmax(start, lag(end, 1, default = 0)+1) + 1))
    },
    r2evans={
        mm <- matrix(c(11,21,31,41,42,54,61,63,
                       20,30,40,50,51,63,70,72), nc=2)
        vec <- pmax(mm[,1], c(0,1+head(mm[,2],n=-1)))
        sum(mm[,2]-vec+1)
    }
    )
##  Unit: microseconds
##       expr     min      lq   median      uq     max neval
##   beginneR 230.410 238.297 244.9015 261.228 443.574   100
##    r2evans  37.791  40.725  44.7620  47.880 147.124   100

这大大受益于使用矩阵而不是data.frames。

哦,系统时间在这里没有用: - )

system.time({
    mm <- matrix(c(11,21,31,41,42,54,61,63,
                   20,30,40,50,51,63,70,72), nc=2)
    vec <- pmax(mm[,1], c(0,1+head(mm[,2],n=-1)))
    sum(mm[,2]-vec+1)
})
##     user  system elapsed 
##        0       0       0