R 为什么使用apply时矩阵的元素乘积要快得多?

R 为什么使用apply时矩阵的元素乘积要快得多?,r,performance,R,Performance,我想把矩阵的所有元素相乘。我可以用两个for循环或apply来完成。我的直觉是for循环会更快。Apply必须创建一个临时向量来存储行乘积的结果,然后将乘积应用于该行 它仍然必须执行for循环以将所有元素相乘,因此这只是存储中间结果的额外操作,而for循环方法不必这样做。但速度仍然是原来的4倍。为什么呢 cols <- 1000 rows <- 1000 a <- matrix(runif(cols * rows, 1, 2), nrow = rows) system.ti

我想把矩阵的所有元素相乘。我可以用两个for循环或apply来完成。我的直觉是for循环会更快。Apply必须创建一个临时向量来存储行乘积的结果,然后将乘积应用于该行

它仍然必须执行for循环以将所有元素相乘,因此这只是存储中间结果的额外操作,而for循环方法不必这样做。但速度仍然是原来的4倍。为什么呢

cols <- 1000
rows <- 1000

a <- matrix(runif(cols * rows, 1, 2), nrow = rows)

system.time({
  result <- 1
  for(i in 1:nrow(a)) {
    for(j in 1:ncol(a)) {
      result <- result * a[i, j]
    }
  }
})
# 0.09s

system.time(result <- prod(apply(a, 1, prod)))
# 0.01s
应用速度似乎更快,因为仍有一些矢量化正在进行。循环考虑:

对我来说,应用的速度和应用的速度差不多。

看起来应用的速度更快,因为还有一些矢量化在进行。循环考虑:

对我来说,这与应用的速度一样快。

要进行矢量化而不是应用,可以使用matrixStats中的RowProd:

矢量化而不是应用大约需要18毫秒。您可以使用matrixStats中的RowProd:


大约需要18毫秒

以下是我在测试各种方法时得到的结果。我对Inf是许多计算的结果这一事实有些担心,我想知道对0-1范围的限制是否会有所不同。像@badmax一样,我对proda的速度相对较慢感到惊讶。在我看来,它应该用C编码,而且效率更高。我还推断,面向列的方法可能比面向行的方法更快,因为这就是R中矩阵的存储方式,并且是正确的:

library(microbenchmark)
cols <- 1000
rows <- 1000
a <- matrix(runif(cols * rows, 1, 2), nrow = rows)

microbenchmark(loop1 ={
                   result <- 1
                   for(i in 1:nrow(a)) {
                      for(j in 1:ncol(a)) {
                        result <- result * a[i, j]
                         }               } }, 
          loop2 ={result <- 1
                    for(j in 1:ncol(a)) {
                         result <- result * prod(a[ , j])
                         }               },
          loop3 = {
                     result <- 1
                     for(i in 1:nrow(a)) {
                        result <- result * prod( a[i, ])
                         }                }, 
         apply_test = {result <- prod(apply(a, 1, prod))},
         prod_test = {result <- prod(a) },
         Reduce_test = {result <- Reduce("*", a)},
         log_sum = { result<- exp( sum(log(a)))}) #since sum of logs == log of prod
#====================
Unit: milliseconds
        expr        min         lq       mean     median         uq       max neval   cld
       loop1  58.872740  59.782277  60.665321  60.246169  61.156176  67.33558   100   c  
       loop2   5.314437   5.843748   7.316167   6.024948   6.626402  57.36532   100 a    
       loop3   9.614727  10.248335  11.521343  10.541872  10.947829  45.08280   100 ab   
  apply_test   8.336721   8.924148   9.960122   9.166424   9.429118  17.35621   100 ab   
   prod_test  94.314333  95.438939  95.956394  95.911858  96.286444  98.54229   100    d 
 Reduce_test 292.907175 312.754959 389.959756 354.369616 511.151578 545.80829   100     e
     log_sum  19.258281  19.916965  22.333617  20.134510  20.551704 180.18492   100  b   

prod函数现在从相对于循环和应用方法的较低位置被解救出来。

以下是我通过对各种方法进行基准测试而得到的结果。我对Inf是许多计算的结果这一事实有些担心,我想知道对0-1范围的限制是否会有所不同。像@badmax一样,我对proda的速度相对较慢感到惊讶。在我看来,它应该用C编码,而且效率更高。我还推断,面向列的方法可能比面向行的方法更快,因为这就是R中矩阵的存储方式,并且是正确的:

library(microbenchmark)
cols <- 1000
rows <- 1000
a <- matrix(runif(cols * rows, 1, 2), nrow = rows)

microbenchmark(loop1 ={
                   result <- 1
                   for(i in 1:nrow(a)) {
                      for(j in 1:ncol(a)) {
                        result <- result * a[i, j]
                         }               } }, 
          loop2 ={result <- 1
                    for(j in 1:ncol(a)) {
                         result <- result * prod(a[ , j])
                         }               },
          loop3 = {
                     result <- 1
                     for(i in 1:nrow(a)) {
                        result <- result * prod( a[i, ])
                         }                }, 
         apply_test = {result <- prod(apply(a, 1, prod))},
         prod_test = {result <- prod(a) },
         Reduce_test = {result <- Reduce("*", a)},
         log_sum = { result<- exp( sum(log(a)))}) #since sum of logs == log of prod
#====================
Unit: milliseconds
        expr        min         lq       mean     median         uq       max neval   cld
       loop1  58.872740  59.782277  60.665321  60.246169  61.156176  67.33558   100   c  
       loop2   5.314437   5.843748   7.316167   6.024948   6.626402  57.36532   100 a    
       loop3   9.614727  10.248335  11.521343  10.541872  10.947829  45.08280   100 ab   
  apply_test   8.336721   8.924148   9.960122   9.166424   9.429118  17.35621   100 ab   
   prod_test  94.314333  95.438939  95.956394  95.911858  96.286444  98.54229   100    d 
 Reduce_test 292.907175 312.754959 389.959756 354.369616 511.151578 545.80829   100     e
     log_sum  19.258281  19.916965  22.333617  20.134510  20.551704 180.18492   100  b   

prod函数现在从相对于循环和应用方法的较低位置被解救出来。

为什么不执行proda?这需要0.13秒,比它们都慢。是的,我在测试时更改了runif的范围。但这确实让我怀疑结果是否与此有关?产品趋向于非常快地变为0或Inf。为什么不做proda?这需要0.13秒,比所有产品都慢。是的,我在测试时更改了runif的范围。但这确实让我怀疑结果是否与此有关?产品倾向于很快变为0或Inf。因此,当产品未达到Inf时,是否可以安全地得出结论,prod是最好的?这是我最初的预期,似乎得到了这一证据的支持。安全我不会直截了当的。这毕竟是统计数据。那么,当产品未达到Inf时,是否可以安全地得出结论,即prod是最好的?这是我最初的预期,似乎得到了这一证据的支持。安全我不会直截了当的。这毕竟是统计数字。
library(microbenchmark)
cols <- 1000
rows <- 1000
a <- matrix(runif(cols * rows, 1, 2), nrow = rows)

microbenchmark(loop1 ={
                   result <- 1
                   for(i in 1:nrow(a)) {
                      for(j in 1:ncol(a)) {
                        result <- result * a[i, j]
                         }               } }, 
          loop2 ={result <- 1
                    for(j in 1:ncol(a)) {
                         result <- result * prod(a[ , j])
                         }               },
          loop3 = {
                     result <- 1
                     for(i in 1:nrow(a)) {
                        result <- result * prod( a[i, ])
                         }                }, 
         apply_test = {result <- prod(apply(a, 1, prod))},
         prod_test = {result <- prod(a) },
         Reduce_test = {result <- Reduce("*", a)},
         log_sum = { result<- exp( sum(log(a)))}) #since sum of logs == log of prod
#====================
Unit: milliseconds
        expr        min         lq       mean     median         uq       max neval   cld
       loop1  58.872740  59.782277  60.665321  60.246169  61.156176  67.33558   100   c  
       loop2   5.314437   5.843748   7.316167   6.024948   6.626402  57.36532   100 a    
       loop3   9.614727  10.248335  11.521343  10.541872  10.947829  45.08280   100 ab   
  apply_test   8.336721   8.924148   9.960122   9.166424   9.429118  17.35621   100 ab   
   prod_test  94.314333  95.438939  95.956394  95.911858  96.286444  98.54229   100    d 
 Reduce_test 292.907175 312.754959 389.959756 354.369616 511.151578 545.80829   100     e
     log_sum  19.258281  19.916965  22.333617  20.134510  20.551704 180.18492   100  b   
Unit: milliseconds
        expr        min         lq       mean     median         uq        max neval  cld
       loop1  56.693831  58.846847  59.688896  59.448108  60.208619  63.005431   100   c 
       loop2   5.667955   5.907125  10.090634   6.109151   6.532552 183.985617   100 ab  
       loop3   9.533779  10.080330  12.760057  10.431867  10.734991 183.262217   100 ab  
  apply_test   8.144015   8.622861   9.940263   8.904425   9.962390  17.470028   100 ab  
   prod_test   1.327710   1.371449   1.411990   1.394160   1.432646   1.677596   100 a   
 Reduce_test 293.697339 312.384739 385.437743 356.997439 500.446356 557.253762   100    d
     log_sum  22.444015  23.224879  24.064932  23.539085  24.210656  29.774315   100  b