2013-06-13 32 views
24

我有一个矩阵m和一个矢量v。我想将矩阵m的第一列乘以矢量v的第一个元素,并将矩阵m的第二列乘以矢量v的第二个元素,依此类推。我可以用下面的代码来完成,但我正在寻找一种不需要两次转置调用的方法。我如何在R中更快地做到这一点?将矩阵列与矢量元素相乘的最快方法R

m <- matrix(rnorm(120000), ncol=6) 
v <- c(1.5, 3.5, 4.5, 5.5, 6.5, 7.5) 

system.time(t(t(m) * v)) 

# user system elapsed 
# 0.02 0.00 0.02 
+0

相关:http://stackoverflow.com/q/3643555/946850 – krlmlr

回答

33

使用一些线性代数和执行矩阵乘法,这是相当快R

m %*% diag(v)

一些基准

m = matrix(rnorm(1200000), ncol=6) 

v=c(1.5, 3.5, 4.5, 5.5, 6.5, 7.5) 
library(microbenchmark) 
microbenchmark(m %*% diag(v), t(t(m) * v)) 
## Unit: milliseconds 
##   expr  min  lq median  uq  max neval 
## m %*% diag(v) 16.57174 16.78104 16.86427 23.13121 109.9006 100 
##  t(t(m) * v) 26.21470 26.59049 32.40829 35.38097 122.9351 100 
+0

Tha的权利,只是它应该是microbenchmark(m%*​​%diag(v),t(t(m)* v)) – rose

+0

事实上,更改@rose – mnel

+1

我发现结果很大程度上取决于'v'。对于较短的'v','diag()'选项更快,但最终双转置获胜。 – krlmlr

3

正如@Arun指出的那样,我不知道你会在时间效率方面超越你的解决方案。在代码的可理解性方面,还有其他的选择,但:

一个选项:

> mapply("*",as.data.frame(m),v) 
     V1 V2 V3 
[1,] 0.0 0.0 0.0 
[2,] 1.5 0.0 0.0 
[3,] 1.5 3.5 0.0 
[4,] 1.5 3.5 4.5 

而另:

sapply(1:ncol(m),function(x) m[,x] * v[x]) 
+0

我怀疑这会比在矩阵上工作要快(特别是你的第一个解决方案)。 – Arun

+0

当我检查大样本的system.time时,它们之间没有区别,它不会更快。 – rose

+0

@rose - 尽管提供了替代方案,但我同意Arun的意见。我不确定't(t(..'解决方案 – thelatemail

15

如果你有一个更大的列数你的T(T(M)* v)溶液优于通过广泛的矩阵乘法解决方案保证金。不过,有一个更快的解决方案,但它的内存使用成本很高。使用rep()创建一个与m相同的矩阵并乘以元素。这里的比较,修改MNEL的例子:

m = matrix(rnorm(1200000), ncol=600) 
v = rep(c(1.5, 3.5, 4.5, 5.5, 6.5, 7.5), length = ncol(m)) 
library(microbenchmark) 

microbenchmark(t(t(m) * v), 
    m %*% diag(v), 
    m * rep(v, rep.int(nrow(m),length(v))), 
    m * rep(v, rep(nrow(m),length(v))), 
    m * rep(v, each = nrow(m))) 

# Unit: milliseconds 
#         expr  min   lq  mean  median   uq  max neval 
#       t(t(m) * v) 17.682257 18.807218 20.574513 19.239350 19.818331 62.63947 100 
#       m %*% diag(v) 415.573110 417.835574 421.226179 419.061019 420.601778 465.43276 100 
# m * rep(v, rep.int(nrow(m), ncol(m))) 2.597411 2.794915 5.947318 3.276216 3.873842 48.95579 100 
#  m * rep(v, rep(nrow(m), ncol(m))) 2.601701 2.785839 3.707153 2.918994 3.855361 47.48697 100 
#    m * rep(v, each = nrow(m)) 21.766636 21.901935 23.791504 22.351227 23.049006 66.68491 100 

正如你可以看到,使用“每个”在代表()牺牲速度的清晰度。 rep.int和rep之间的区别似乎是可以忽略的,两个实现在重复运行microbenchmark()时交换位置。请记住,ncol(m)==长度(v)。

autoplot

+0

请注意,双转置也至少复制矩阵一次,不知道内存使用是否比仅扩展矩阵好得多,扩展本身可以使用'矩阵(v,nrow = nrow(m) ,ncol = ncol(m),byrow = TRUE)'。 – krlmlr

+0

关于您编写​​的'rep'解决方案“...内存使用成本很高”。 't(m)'不会产生相同的成本,因为这会创建一个与'm'具有相同元素数量的新矩阵? – jochen

1

如bluegrue完成的,一个简单的代表就足够,以及执行逐元素乘法。

乘法和求和的次数大幅减少,就好像简单矩阵乘法与diag()一样,对于这种情况,可以避免大量的零乘。

m = matrix(rnorm(1200000), ncol=6) 
v=c(1.5, 3.5, 4.5, 5.5, 6.5, 7.5) 
v2 <- rep(v,each=dim(m)[1]) 
library(microbenchmark) 
microbenchmark(m %*% diag(v), t(t(m) * v), m*v2) 

Unit: milliseconds 
      expr  min  lq  mean median  uq  max neval cld 
m %*% diag(v) 11.269890 13.073995 16.424366 16.470435 17.700803 95.78635 100 b 
    t(t(m) * v) 9.794000 11.226271 14.018568 12.995839 15.010730 88.90111 100 b 
     m * v2 2.322188 2.559024 3.777874 3.011185 3.410848 67.26368 100 a 
1

为了完整起见,我将sweep添加到基准。尽管有点误导属性名,我认为这可能是比其他替代更具可读性,也相当快:

n = 1000 
M = matrix(rnorm(2 * n * n), nrow = n) 
v = rnorm(2 * n) 

microbenchmark::microbenchmark(
    M * rep(v, rep.int(nrow(M), length(v))), 
    sweep(M, MARGIN = 2, STATS = v, FUN = `*`), 
    t(t(M) * v), 
    M * rep(v, each = nrow(M)), 
    M %*% diag(v) 
) 

Unit: milliseconds 
             expr   min   lq  mean 
    M * rep(v, rep.int(nrow(M), length(v))) 5.259957 5.535376 9.994405 
sweep(M, MARGIN = 2, STATS = v, FUN = `*`) 16.083039 17.260790 22.724433 
           t(t(M) * v) 19.547392 20.748929 29.868819 
       M * rep(v, each = nrow(M)) 34.803229 37.088510 41.518962 
           M %*% diag(v) 1827.301864 1876.806506 2004.140725 
     median   uq  max neval 
    6.158703 7.606777 66.21271 100 
    20.479928 23.830074 85.24550 100 
    24.722213 29.222172 92.25538 100 
    39.920664 42.659752 106.70252 100 
1986.152972 2096.172601 2432.88704 100