2013-06-13 35 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