2016-05-12 146 views
4

首先,我是一個新手用戶,所以忘記了我的一般無知。我正在尋找一種更快的替代方法來運行R中的%*%運算符。儘管較舊的帖子提示使用RcppArmadillo,但我已經嘗試了2個小時來使RcppArmadillo無法成功運行。我總是遇到產生'意外...'錯誤的詞彙問題。我發現在RCPP下面的功能,我可以讓工作:Rcpp中的矩陣乘法

library(Rcpp) 
func <- ' 
NumericMatrix mmult(NumericMatrix m , NumericMatrix v, bool byrow=true) 
{ 
    if(! m.nrow() == v.nrow()) stop("Non-conformable arrays") ; 
    if(! m.ncol() == v.ncol()) stop("Non-conformable arrays") ; 

    NumericMatrix out(m) ; 

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

這個功能,但是,進行逐元素乘法和不表現爲%*%。有沒有簡單的方法來修改上面的代碼來實現預期的結果?

編輯:

我已經想出了使用RcppEigen似乎擊敗%*%功能:

etest <- cxxfunction(signature(tm="NumericMatrix", 
          tm2="NumericMatrix"), 
       plugin="RcppEigen", 
       body=" 
NumericMatrix tm22(tm2); 
NumericMatrix tmm(tm); 

const Eigen::Map<Eigen::MatrixXd> ttm(as<Eigen::Map<Eigen::MatrixXd> >(tmm)); 
const Eigen::Map<Eigen::MatrixXd> ttm2(as<Eigen::Map<Eigen::MatrixXd> >(tm22)); 

Eigen::MatrixXd prod = ttm*ttm2; 
return(wrap(prod)); 
       ") 

set.seed(123) 
M1 <- matrix(sample(1e3),ncol=50) 
M2 <- matrix(sample(1e3),nrow=50) 

identical(etest(M1,M2), M1 %*% M2) 
[1] TRUE 
res <- microbenchmark(
+ etest(M1,M2), 
+ M1 %*% M2, 
+ times=10000L) 

res 

Unit: microseconds 
     expr min lq  mean median  uq max neval 
etest(M1, M2) 5.709 6.61 7.414607 6.611 7.211 49.879 10000 
    M1 %*% M2 11.718 12.32 13.505272 12.621 13.221 58.592 10000 
+2

的評論是部分不正確在RcppEigen _不_依靠系統複製Eigen,但帶來自己的,類似於其他R包。 –

+0

啊,謝謝你的澄清,@DirkEddelbuettel。很高興知道。我認爲這是一個包裝。 – RHertel

回答

1

我會鼓勵嘗試與RcppArmadillo制定出你的問題。使用它,因爲這例子也通過調用RcppArmadillo.package.skeleton()創建爲簡單:

// another simple example: outer product of a vector, 
// returning a matrix 
// 
// [[Rcpp::export]] 
arma::mat rcpparma_outerproduct(const arma::colvec & x) { 
    arma::mat m = x * x.t(); 
    return m; 
} 

// and the inner product returns a scalar 
// 
// [[Rcpp::export]] 
double rcpparma_innerproduct(const arma::colvec & x) { 
    double v = arma::as_scalar(x.t() * x); 
    return v; 
} 

有一個在實際例子更多的代碼,但是這應該給你一個想法。

+0

謝謝你的回答,德克。這是我調用'RcppArmadillo.package.skeleton()'後在第一行得到的結果:錯誤:「/」中出現意外的'/'。 Rcpp和RcppArmadillo軟件包已安裝,我按照說明進行操作。 – David

+1

您需要將它作爲包(和目錄)名稱的參數:'RcppArmadillo.package.skeleton(「mypackage」)'。 –

+0

再次感謝,德克。我仍然得到同樣的錯誤。命令如'rcpp_hello_world < - function(){。調用(「rcpp_hello_world」,PACKAGE =「mypackage」)}'工作正常,但只要我改變R語法,它就不會識別它們。任何具有可重複示例的傻瓜教程? – David

6

有充分的理由依賴現有的庫/包進行標準任務。在庫中的例程是

  • 優化
  • 徹底測試
  • 的良好手段保持代碼的緊湊,人類可讀的,並且易於維護。

因此,我認爲使用RcppArmadillo或RcppEigen應該在這裏更好。但是,爲了回答你的問題,下面是一個可能的RCPP代碼來執行矩陣乘法:

library(Rcpp) 
cppFunction('NumericMatrix mmult(const NumericMatrix& m1, const NumericMatrix& m2){ 
if (m1.ncol() != m2.nrow()) stop ("Incompatible matrix dimensions"); 
NumericMatrix out(m1.nrow(),m2.ncol()); 
NumericVector rm1, cm2; 
for (size_t i = 0; i < m1.nrow(); ++i) { 
    rm1 = m1(i,_); 
    for (size_t j = 0; j < m2.ncol(); ++j) { 
     cm2 = m2(_,j); 
     out(i,j) = std::inner_product(rm1.begin(), rm1.end(), cm2.begin(), 0.);    
    } 
    } 
return out; 
}') 

測試一下:

A <- matrix(c(1:6),ncol=2) 
B <- matrix(c(0:7),nrow=2) 
mmult(A,B) 
#  [,1] [,2] [,3] [,4] 
#[1,] 4 14 24 34 
#[2,] 5 19 33 47 
#[3,] 6 24 42 60 
identical(mmult(A,B), A %*% B) 
#[1] TRUE 

希望這有助於。


作爲基準測試表明,上面的RCPP代碼比較慢的r內置%*%運算符。我認爲,雖然我RCPP代碼肯定能得到改善,這將是很難被擊敗的背後%*%優化的代碼在性能方面:

library(microbenchmark) 
set.seed(123)  
M1 <- matrix(rnorm(1e4),ncol=100) 
M2 <- matrix(rnorm(1e4),nrow=100) 
identical(M1 %*% M2, mmult(M1,M2)) 
#[1] TRUE 
res <- microbenchmark(
      mmult(M1,M2), 
      M1 %*% M2, 
      times=1000L) 
#> res 
#Unit: microseconds 
#   expr  min  lq  mean median  uq  max neval cld 
# mmult(M1, M2) 1466.855 1484.8535 1584.9509 1494.0655 1517.5105 2699.643 1000 b 
#  M1 %*% M2 602.053 617.9685 687.6863 621.4335 633.7675 2774.954 1000 a 
+1

謝謝RHertel,非常有趣。 C++語法對我來說很具挑戰性。我使用RcppEigen設法擊敗了%*%,請參閱我的第一篇文章中的編輯。 – David

+0

好。正如我在第一條評論和答案中所說的那樣,使用RcppEigen是一種好方法 - 尤其是如果您不想自己編寫算法。不需要重新發明輪子;-) – RHertel