【问题标题】:Speed up r loop string match (vector vs. data.frame)加快 r 循环字符串匹配(向量与 data.frame)
【发布时间】:2017-04-13 03:35:49
【问题描述】:

我正在尝试优化 r 中的循环,该循环计算关于数据帧中每一行的向量中每个元素的字符串匹配数。在小型数据集中它工作得很好(约 15 分钟;11 列,914 行)。但是,在庞大的数据集(914 列,18.000 行)中运行需要几天时间。这是我非常基本的循环:

for (j in 1: dim(pddbnh)[1]){
  for (i in 1:dim(pidf)[1]){
    richa[i,j] <- length(pidf[i,][pidf[i,] == row.names(pddbnh)[j] ])
   }
}

我想知道是否有人知道如何使用其他方法(例如矢量化)优化此循环。任何解决方案将不胜感激!

更新 这是一个小数据集。这是最快的一个

 df<-data.frame(replicate(10,sample(c("sp1", "sp2"),10,rep=TRUE)))
 vec<-c("sp1", "sp2")
 richa <- data.frame()

  for (j in 1:length(vec)){
    for (i in 1:dim(df)[1]){
     richa[i,j] <- length(df[i,][df[i,] == vec[j] ])
     }
    }

【问题讨论】:

  • 是的,我可能知道该怎么做。请提供一个最小的可重现示例进行测试。
  • (显然你还没有彻底搜索 SO,因为有很多关于提高 for 循环性能的 Q&A。)要求 [ 处理 [ pidf[i,] == row.names(pddbnh)[j] ] 似乎注定要失败.在强制之后,该参数的结果将是 0 或 1(因此应该出现有关替换长度为零的错误)。也许您应该解释一下您实际尝试的是什么。
  • 其实它比其他函数(例如 grep)效果更好。我只是想计算在数据帧 (pidf[i,]) 的每一行中找到字符串 (row.names(pddbnh)[j]) 的次数。
  • 发布一个小例子(... dropbox 不再是共享数据的友好方式)。也许这个例子会说明为什么一个逻辑索引如果返回 FALSE 就不会产生错误。

标签: r loops for-loop match vectorization


【解决方案1】:

这是使用lapply 的方法(见下文更快):

richa <- lapply( X = vec, FUN = function(x) rowSums( df == x ) )
richa <- do.call( cbind, richa )

在您提供的小型数据集上的快速 microbenchmark 显示,这比您的 for 循环方法快了大约 10 倍。

补充一点,对于非常大的数据集,这也可以很容易地实现多线程,使用parallel::mclapply 或plyr::laply(使用parallel = TRUE)。这需要一些额外的工作,但对于您拥有的 18000 x 914 数据集来说可能是值得的。

编辑添加:因为你有几个 for 循环在那里(而且因为我正在学习 Rcpp,并且热衷于练习)这里是使用 Rcpp 的更快的解决方案。这是函数定义(需要编译一次):

Rcpp::cppFunction(' IntegerMatrix charCrossCheck( CharacterMatrix df,
                          CharacterVector vec ) {

              IntegerMatrix output( df.nrow(), vec.size() );

              for (int j=0; j < vec.size(); ++j ){
                  for (int i=0; i < df.nrow(); ++i ){
                      int count = 0;
                      for( int k=0; k < df.ncol(); k++ ){
                          if( df(i,k) == vec[j] ) {
                              count++;
                          }
                      }
                      output(i,j) = count;
                  }
              }
              return output;

              } ')

然后你可以调用这个函数:

richa <- charCrossCheck( as.matrix(df), vec )

Rcpp 在这里非常快。您非常小的样本上的 Microbenchmark 显示它比我上面的 lapply 解决方案快 3 倍以上,比 R 中的 for 循环快约 38 倍。

有趣的是,将输入数据扩展为大小为 4000x4000 的 df 和长度为 10 的 vec,Rcpp 和 lapply 方法都在非常相似的时间内完成了这项工作(分别为 3.4 和 3.9 秒)。在您提到的大小的数据集上(18000 行 x 914 列,vec 长度为 2),两种解决方案都远低于 1 秒。无论哪种方式都不错!

【讨论】:

  • 这两种方法都相当快!这太棒了,对我帮助很大。我将尝试找出这两种实现的细节。我很惊讶!
猜你喜欢
  • 1970-01-01
  • 2011-04-28
  • 2016-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-17
相关资源
最近更新 更多