【问题标题】:Efficient dataframe iteration in RR中的高效数据帧迭代
【发布时间】:2014-09-16 07:41:43
【问题描述】:

假设我有一个 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]))
}))))

在我的 500 万行数据帧上,这证明非常慢。

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

用户系统已过 19.946 0.620 20.477

【问题讨论】:

  • 如果您有 500 万行数字数据,您应该使用 matrix,而不是 data.frame
  • 新向量的长度不只是差值的总和(加1)吗? sum(df$end - df$start + 1)?
  • @konvas;不像有些开始是在前一个结束之前
  • 不,因为有重叠。在df数据帧中,第四行是41-50,第五行是42-51。根据您的方法,唯一数字的总数将是 (50-41+1)+(51-42+1) = 20,而应该是 41-51,或 (51-41+1) = 11。跨度>
  • @Richard Scriven 好的,即使转换为矩阵,也应该有更快的方法。顺便说一句,我正在编写的脚本的另一部分已经生成了一个数据框。

标签: r


【解决方案1】:

假设您的数据已排序,这应该可以工作。

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 答案中的最大持续时间与所有其他值(非常快)相比如此之高

【讨论】:

  • 我也看到了最高时间。我运行了几次,发现我和你的异常值都出现了,次数差不多。也许我们看到了不方便定时垃圾收集的工件。 耸耸肩
  • 为了获得更准确的基准,无论如何都需要更多地了解实际数据。如果 OP 可以提供一些,那就太好了,尽管接下来的几个小时我不会在我的办公桌前进行测试。
  • @beginneR 好吧,我给你。我觉得这很有趣。我会将 .RData 文件链接到 Dropbox。请注意,您可能需要一些软件包,例如 Biostrings(只需在 Google 上快速搜索如何下载)、IRanges、BiocGenerics、parallel。所以继续狂奔吧!记住!您必须先按起始位置订购! dropbox.com/s/l5670r02jbyl9mx/data.RData。而且,是的,只需使用load(file=),它将作为变量 original.data 加载
  • @r2evans 好吧,我给你。我觉得这很有趣。我会将 .RData 文件链接到 Dropbox。请注意,您可能需要一些软件包,例如 Biostrings(只需在 Google 上快速搜索如何下载)、IRanges、BiocGenerics、parallel。所以继续狂奔吧!记住!您必须先按起始位置订购! dropbox.com/s/l5670r02jbyl9mx/data.RData。而且,是的,只需使用load(file=),它将作为变量 original.data 加载
  • @user3855285,由于某种原因,我无法加载您的数据。既然你的问题已经解决了,也许你可以继续选择一个被接受的答案,对吧?
【解决方案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 调用后,我对我的实现进行了快速基准测试,与其他答案相比(这也是正确的):

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 

【讨论】:

  • 不错的基准测试,虽然您没有在函数中包含 mm &lt;- as.matrix(df)
  • 很好,但现在我包括了所有三个数据的形成,它仍然将matrix 实现放在前面。编辑即将发布...
  • 我认为这是不对的,因为数据已经在 data.frame 中,所以我的答案不需要转换(或创建)data.frame,而它是必需的convert 将 data.frame 转换为矩阵以获得您的答案..
  • 有趣的是,我们正在努力缩短执行时间……如果数据已经形成(您的基准测试),现在使用矩阵而不是数据仅能提高 25% 的速度。帧 -- 我印象深刻和惊讶(dplyr 真的很神奇)。
  • 我不确定这与dplyr 本身有多大关系
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 2021-11-01
  • 2018-10-10
相关资源
最近更新 更多