将矩阵乘以向量的最快方法

ros*_*ose 13 r

我有一个矩阵mat和一个向量v.我想将第一列矩阵乘以mat向量的第一个元素,v并将第二列矩阵乘以向量的第二mat个元素v.我可以如图所示做到这一点.如果我们得到一个大矩阵,我怎样才能在R中更快地做到这一点?

    mat = matrix(rnorm(1500000), ncol= 100)
    v= rnorm(100)
    > system.time( mat %*% diag(v))
      user  system elapsed 
      0.02    0.00    0.02 
Run Code Online (Sandbox Code Playgroud)

Joh*_*ohn 14

回收可以使它更快,但你可以在列内循环,而不是跨越,所以只需转置和转置.

t( t(mat) * v )
Run Code Online (Sandbox Code Playgroud)

这应该比sweep或更快%*%.

microbenchmark(mat %*% diag(v),sweep(mat, 2, v, FUN = "*"), t(t(mat)*v))
Unit: milliseconds
            expr       min        lq    median        uq      max neval
             %*% 150.47301 152.16306 153.17379 161.75416 281.3315   100
           sweep  35.94029  42.67210  45.53666  48.07468 168.3728   100
   t(t(mat) * v)  16.50813  23.41549  26.31602  29.44008 160.1651   100
Run Code Online (Sandbox Code Playgroud)


Sim*_*lon 12

游戏有点晚了,但有人说最快吗?!这可能是另一个很好用的Rcpp.mmult默认情况下,此函数(被调用)将矩阵的每一列乘以向量的每个连续元素,但可以通过设置选择按列进行此操作byrow = FALSE.它还根据选项检查该尺寸m并且v尺寸合适byrow.无论如何,它的速度很快(比最好的原生R答案快10-12倍)......

编辑

@chris为我提出的另一个问题提供了这个很好的答案,试图让它与之合作RcppArmadillo.然而,Rcpp我在这里发布的-only函数似乎仍然比这快8倍,比OP方法快约70倍.单击@chris'功能代码的链接 - 它非常简单.

我将基准测试放在顶部..

require( microbenchmark )
m <- microbenchmark( mat %*% diag(v) , mmult( mat , v ) , sweep(mat, 2, v, FUN = "*") , chris( mat , v ) , t( t(mat) * v ) , times = 100L )
print( m , "relative" , order = "median" , digits = 3 )
Unit: relative
                        expr   min    lq median    uq   max neval
               mmult(mat, v)  1.00  1.00   1.00  1.00  1.00   100
               chris(mat, v) 10.74  9.31   8.15  7.27 10.44   100
               t(t(mat) * v)  9.65  8.75   8.30 15.33  9.52   100
 sweep(mat, 2, v, FUN = "*") 20.51 18.35  22.18 21.39 16.94   100
             mat %*% diag(v) 80.44 70.11  73.12 70.68 54.96   100
Run Code Online (Sandbox Code Playgroud)

浏览以了解如何mmult工作并返回与OP相同的结果...

require( Rcpp )

#  Source code for our function
func <- 'NumericMatrix mmult( NumericMatrix m , NumericVector v , bool byrow = true ){
  if( byrow );
    if( ! m.nrow() == v.size() ) stop("Non-conformable arrays") ;
  if( ! byrow );
    if( ! m.ncol() == v.size() ) stop("Non-conformable arrays") ;

  NumericMatrix out(m) ;

  if( byrow ){
    for (int j = 0; j < m.ncol(); j++) {
      for (int i = 0; i < m.nrow(); i++) {
        out(i,j) = m(i,j) * v[j];
      }
    }
  }
  if( ! byrow ){
    for (int i = 0; i < m.nrow(); i++) {
      for (int j = 0; j < m.ncol(); j++) {
        out(i,j) = m(i,j) * v[i];
      }
    }
  }
  return out ;
}'

#  Make it available
cppFunction( func )

#  Use it
res1 <- mmult( m , v )

#  OP function
res2 <- mat %*% diag(v)

#  Same result?
identical( res1 , res2 ) # Yes!!
[1] TRUE
Run Code Online (Sandbox Code Playgroud)


Das*_*son 4

sweep似乎在我的机器上运行得更快一些

sweep(mat, 2, v, FUN = "*")
Run Code Online (Sandbox Code Playgroud)

一些基准:

> microbenchmark(mat %*% diag(v),sweep(mat, 2, v, FUN = "*"))

Unit: milliseconds
  expr       min        lq   median        uq      max neval
   %*% 214.66700 226.95551 231.2366 255.78493 349.1911   100
 sweep  42.42987  44.72254  62.9990  70.87403 127.2869   100
Run Code Online (Sandbox Code Playgroud)

  • @rose 对我来说``sweep```` 在 OSX 下的 R 中更快,但在 ubuntu 下更慢。一般来说,我认为你将很难在 ``%*%``` 上做出很大的改进,因为它是直接用 C 实现的(src/main/array.c 的第 610 行)。我想您可以从该函数中删除一些健全性检查,但收益可能非常小。 (2认同)