【问题标题】:Faster i, j matrix cell fill更快的 i, j 矩阵单元填充
【发布时间】:2014-07-12 02:40:47
【问题描述】:

我想获取 data.frame/matrix 的列,并在数据帧的每个单元格 ([i, j]) 之间应用一个函数,其中 i 和 j 是沿 data.frame 列的序列。基本上,我想以与cor 函数与 data.frame 一起使用的相同方式填充单个单元格的矩阵。

这是一个相关问题:Create a matrix from a function and two numeric data frames 但是,我在随机化测试中使用它并多次重复操作(制作许多矩阵)。我正在寻找执行此操作的最快方法。我使用并行处理加快了速度,但我仍然对这种速度不满意。也不能假设矩阵输出是对称的,即cor 产生对称矩阵的方式(我的示例将反映这一点)。

我今天在 data.table 网页上看到了以下内容 (http://datatable.r-forge.r-project.org/):

DF[i,j]<-value快500+倍

这让我想到data.tabledplyr 或其他方式可能会加快速度。我的大脑一直专注于填充细胞,但也许有更好的方法涉及重塑、应用功能和重塑矩阵或类似的东西。我可以使用outerfor 循环在base R 中实现这一点,如下所示。

## Arbitrary function
FUN <- function(x, y) round(sqrt(sum(x)) - sum(y), digits=1)

## outer approach
outer(
  names(mtcars), 
  names(mtcars), 
  Vectorize(function(i,j) FUN(mtcars[,i],mtcars[,j]))
)

## for approach
mat <- matrix(rep(NA, ncol(mtcars)^2), ncol(mtcars))
for (i in 1:ncol(mtcars)) {
    for (j in 1:ncol(mtcars)) {
        mat[i, j] <- FUN(mtcars[, i], mtcars[, j])
    }
}
mat

以下是microbenchmark 的时间安排,for 略有优势。

Unit: milliseconds
    expr      min       lq   median       uq      max neval
 OUTER() 4.450410 4.691124 4.774394 4.877724 55.77333  1000
   FOR() 4.309527 4.521785 4.588728 4.694156  7.04275  1000

R 中最快的方法是什么(欢迎添加软件包)?

【问题讨论】:

  • 你的真实数据集的维度大约是多少?
  • 变化很大,从 10 X 10 到 1000 X 1000
  • 既然你说函数是任意的,那么函数本身是否可以向量化就不好说了。我的意思是,在这里,您可以计算一次colSums(mat),一次计算sqrt(.),然后使用expand.grid(或CJ——更快,从data.table)生成组合。在不知道可能的矢量化数量的情况下,我只能建议 C 或 Rcpp。
  • @Arun 这很明智。我一直在寻找可以与任何功能一起使用的通用代码。我认为CJ 的想法可能会给我一些尝试和替补的东西。但是是的,函数上的矢量离子非常重要。
  • 使用 .subset2(mtcars, i) 而不是 mtcars[, i],您将获得 4-5 倍的加速

标签: r performance data.table dplyr


【解决方案1】:

仍然坚持使用base R 解决方案,我在基于for 的方法中获得了 1.6-1.7 倍的加速:

  • [,i] 代替[[i]](显着的时间影响- 可能FUN 只是在这里接收C 指针而不是新分配的向量);
  • FUN 的字节码编译(时间影响小);
  • for代码包装到函数+字节码编译(时间影响小);

顺便说一句,在 2 个循环中交换索引 (i,j) -> (j,i) 不会导致显着差异(理论上,逐行矩阵访问应该更快)。

代码:

library(compiler)
FUN2 <- cmpfun(FUN)
for2 <- cmpfun(function(mtcars, FUN) {
      mat <- matrix(rep(NA, ncol(mtcars)^2), ncol(mtcars))
   for (i in 1:ncol(mtcars)) {
       for (j in 1:ncol(mtcars)) {
           mat[i, j] <- FUN(mtcars[[i]], mtcars[[j]])
       }
   }
   mat
})

基准测试:

 Unit: milliseconds
                min       lq   median       uq      max neval
 outer     7.791739 7.991474 8.245869 8.538163 16.24460   100
 for       8.143679 8.463249 8.588230 9.912008 16.30842   100
 for-mods  4.713837 4.875972 5.006202 5.246584 15.66491   100

在我看来,很难找到更快的方法(但我可能错了)。与多次计算 FUN 所需的时间相比,for 循环时间偏差非常小(大约 0.25 毫秒)。

【讨论】:

  • 不错。即使其他人提出了更快的方法,我也了解到[[i]][, i] 更快。 +1
猜你喜欢
  • 2019-05-22
  • 1970-01-01
  • 1970-01-01
  • 2020-04-19
  • 2018-01-23
  • 1970-01-01
  • 2021-11-03
  • 2021-07-19
  • 1970-01-01
相关资源
最近更新 更多