使用Armadillo基于行索引和列索引从矩阵中提取元素

时间:2016-05-27 21:55:10

标签: c++ r rcpp armadillo

在R中,我可以根据其索引提取矩阵元素,如下所示

> m <- matrix(1:6, nrow = 3)
> m
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6
> row_index <- c(1, 2)
> col_index <- c(2, 2)
> m[cbind(row_index, col_index)]
[1] 4 5

Armadillo / Rcpp :: Armadillo有原生方式吗?我能做的最好的是一个自定义函数,它使用行和列索引来计算元素索引(见下文)。我大多担心自定义功能也不会表现良好。

#include <RcppArmadillo.h>
using namespace Rcpp;

// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
NumericVector Rsubmatrix(arma::uvec rowInd, arma::uvec colInd, arma::mat m) {
  arma::uvec ind = (colInd - 1) * m.n_rows + (rowInd - 1);
  arma::vec ret = m.elem(ind);
  return wrap(ret);
}

/*** R
Rsubmatrix(row_index, col_index, m)
/

2 个答案:

答案 0 :(得分:1)

来自docs

X.submat( vector_of_row_indices, vector_of_column_indices )

但似乎只返回矩阵块。对于非简单连接的区域,我认为您的解决方案是最好的,但您并不真正需要一个函数,

m.elem((colInd - 1) * m.n_rows + (rowInd - 1));

返回矢量没有任何问题。为清楚起见,您可以定义一个函数来处理行+ col到索引的转换,

inline arma::uvec arr2ind(arma::uvec c, arma::uvec r, int nrow) 
{ 
  return c * nrow + r;
}
// m.elem(arr2ind(colInd - 1, rowInd - 1, m.n_rows));

答案 1 :(得分:1)

我们试试吧......

特别是,您可以通过编写自己的循环来rowIndcolInd进行子集,以使用.(i,j)子集运算符。否则,唯一的另一个选择是您建议开始提出问题的解决方案......

#include <RcppArmadillo.h>
using namespace Rcpp;

// [[Rcpp::depends(RcppArmadillo)]]

// Optimized OP method
// [[Rcpp::export]]
arma::vec Rsubmatrix(const arma::mat& m, const arma::uvec& rowInd, const arma::uvec& colInd) {
  return  m.elem((colInd - 1) * m.n_rows + (rowInd - 1));
}

// Proposed Alternative
// [[Rcpp::export]]
arma::rowvec get_elements(const arma::mat& m, const arma::uvec& rowInd, const arma::uvec& colInd){

  unsigned int n = rowInd.n_elem;

  arma::rowvec out(n);

  for(unsigned int i = 0; i < n; i++){
    out(i) = m(rowInd[i]-1,colInd[i]-1);
  }

  return out;
}

其中:

m <- matrix(1:6, nrow = 3) 

row_index <- c(1, 2)
col_index <- c(2, 2)

m[cbind(row_index, col_index)]

给出:

[1] 4 5

我们有:

get_elements(m, row_index, col_index)

,并提供:

     [,1] [,2]
[1,]    4    5

修改

微基准:

microbenchmark(Rsubmatrix(m, row_index, col_index), get_elements(m, row_index, col_index), times = 1e4)

给予:

Unit: microseconds
                                  expr   min    lq     mean median    uq      max neval
   Rsubmatrix(m, row_index, col_index) 2.836 3.111 4.129051  3.281 3.502 5016.652 10000
 get_elements(m, row_index, col_index) 2.699 2.947 3.436844  3.115 3.335  716.742 10000

这些方法都是时间紧密的。请注意,后者应该更好,因为它避免了两个独立的循环(1.计算&amp; 2.到子集)以及为存储结果而创建的附加临时向量。

修改

Per armadillo 7.200.0发布时,sub2ind()函数已获得采用矩阵表示法的功能。此函数通过2 x n矩阵获取矩阵下标,其中n表示要子集的元素数,并将它们转换为元素表示法。

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
arma::rowvec matrix_locs(arma::mat M, arma::umat locs) {

    arma::uvec eids = sub2ind( size(M), locs ); // Obtain Element IDs
    arma::vec v  = M.elem( eids );              // Values of the Elements

    return v.t();                               // Transpose to mimic R
}

调用 R

cpp_locs <- locs - 1       # Shift indices from R to C++

(cpp_locs <- t(cpp_locs))  # Transpose matrix for 2 x n form

matrix_locs(M, cpp_locs)   # Subset the matrix