2017-09-02 73 views
5

我面臨以下問題:我需要一個大矩陣的多個子集。其實我只需要視圖作爲另一個函數f()的輸入,所以我不需要改變這些值。然而,看起來,R對於這項任務來說非常緩慢,或者我做錯了什麼(這看起來更可能)。玩具例子說明了只需要多少時間來選擇列,然後在另一個函數中使用它們(在這個玩具例子中是基本函數sum())。作爲「基準」,我還測試了總結整個矩陣的計算時間,這是驚人的更快。我也嘗試過使用package ref,但是無法獲得任何性能提升。 所以關鍵的問題是如何對矩陣進行子集化而不復制它?我感謝任何幫助,謝謝!R中矩陣的快速子集問題

library(microbenchmark) 
library(ref) 

m0 <- matrix(rnorm(10^6), 10^3, 10^3) 
r0 <- refdata(m0) 
microbenchmark(m0[, 1:900], sum(m0[, 1:900]), sum(r0[,1:900]), sum(m0)) 
Unit: milliseconds 
      expr  min  lq  mean median  uq 
     m0[, 1:900] 10.087403 12.350751 16.697078 18.307475 19.054157 
sum(m0[, 1:900]) 11.067583 13.341860 17.286514 19.123748 19.990661 
sum(r0[, 1:900]) 11.066164 13.194244 16.869551 19.204434 20.004034 
      sum(m0) 1.015247 1.040574 1.059872 1.049513 1.067142 
     max neval 
58.238217 100 
25.664729 100 
23.505308 100 
    1.233617 100 

總結整個矩陣的基準任務需要1.059872毫秒,比其他功能快約16倍。

+0

我得到一個錯誤時,我嘗試你的例子:'r0 [,1:900]中的錯誤:維數不正確# – 5th

+0

'庫('ref')'修復了它 – dvantwisk

+0

好吧,那麼解決方案的標準是什麼?它是否必須比現在做的更快,還是比總結整個矩陣要快? –

回答

4

您的解決方案的問題是子集分配另一個矩陣,這需要時間。

有兩種解決方法:

如果與sum對整個矩陣所花費的時間是與你沒關係,你可以對整個矩陣使用colSums和其子集的結果:

sum(colSums(m0)[1:900]) 

或者你可以使用Rcpp在不復制矩陣的情況下使用子集計算sum

#include <Rcpp.h> 
using namespace Rcpp; 

// [[Rcpp::export]] 
double sumSub(const NumericMatrix& x, 
       const IntegerVector& colInd) { 

    double sum = 0; 

    for (IntegerVector::const_iterator it = colInd.begin(); it != colInd.end(); ++it) { 
    int j = *it - 1; 
    for (int i = 0; i < x.nrow(); i++) { 
     sum += x(i, j); 
    } 
    } 

    return sum; 
} 

    microbenchmark(m0[, 1:900], sum(m0[, 1:900]), sum(r0[,1:900]), sum(m0), 
        sum(colSums(m0)[1:900]), 
        sumSub(m0, 1:900)) 
Unit: milliseconds 
        expr  min  lq  mean median  uq  max neval 
      m0[, 1:900] 4.831616 5.447749 5.641096 5.675774 5.861052 6.418266 100 
     sum(m0[, 1:900]) 6.103985 6.475921 7.052001 6.723035 6.999226 37.085345 100 
     sum(r0[, 1:900]) 6.224850 6.449210 6.728681 6.705366 6.943689 7.565842 100 
       sum(m0) 1.110073 1.145906 1.175224 1.168696 1.197889 1.269589 100 
sum(colSums(m0)[1:900]) 1.113834 1.141411 1.178913 1.168312 1.201827 1.408785 100 
     sumSub(m0, 1:900) 1.337188 1.368383 1.404744 1.390846 1.415434 2.459361 100 

您可以使用unrolling optimization來進一步優化Rcpp版本。

+0

是的,這是答案。 +1。我認爲我的評論在我們實現它之前調用它也值得+1太tho;)真的,如果'sum(colSums(m0)[1:900])'做得那麼好,我們甚至不需要更麻煩的' Rcpp'。 –

+0

感謝您的迴應。是的,我認爲你是絕對正確的。主要問題是爲矩陣的副本分配內存。是否可以在不使用矩陣的情況下在R中執行此操作?我需要矩陣子集作爲另一個自寫函數的輸入,sum()函數只是作爲示例在這裏處理。 – maxatSOflow

+0

基本上,我做了我所有的功能,以便他們可以在矩陣的子集上運用。如果你不想複製,你只需要這樣的功能。 –

0

使用compiler我寫的一樣快得約2倍的結果作爲其他方法(8X是的sum(m0)而不是16倍)的函數:

require(compiler) 

compiler_sum <- cmpfun({function(x) { 
    tmp <- 0 
    for (i in 1:900) 
     tmp <- tmp+sum(x[,i]) 
    tmp 
}}) 

microbenchmark( 
       sum(m0), 
       compiler_sum(m0) 
       ) 
Unit: milliseconds 
      expr  min  lq  mean median  uq  max 
      sum(m0) 1.016532 1.056030 1.107263 1.084503 1.11173 1.634391 
compiler_sum(m0) 7.655251 7.854135 8.000521 8.021107 8.29850 16.760058 
neval 
    100 
    100