2014-11-16 83 views
7

我有一个big.matrix类的对象,其中R的尺寸为778844 x 2。这些值都是整数(公里)。我的目标是使用big.matrix来计算欧几里得距离矩阵,并且因此产生类big.matrix的对象。我想知道是否有一个最佳的方式来做到这一点。使用big.matrix对象计算欧几里德距离矩阵

我选择使用类big.matrix的原因是内存限制。我可以将我的big.matrix转换为类matrix的对象,并使用dist()计算欧几里得距离矩阵。但是,dist()会返回一个不会在内存中分配的大小对象。

编辑

以下的答案必须由约翰·W·爱默生的bigmemory包的作者和维护者给出:

你可以使用大代数我希望,但这也将是一个通过sourceCpp()非常好的Rcpp用例,非常简短。但总之,我们甚至不试图提供高级功能(除了我们作为概念验证实现的基础知识之外)。一旦开始说内存不足,没有一种算法可以覆盖所有用例。

+0

没有下面的回答解决问题了吗?如果是这样,请接受它或相应地更新您的问题。 – cdeterman

回答

7

这是一种使用RcppArmadillo的方法。其中很大一部分与RcppGallery example非常相似。这将返回big.matrix与相关的成对(逐行)欧几里德距离。我想换我big.matrix功能于一身的包装函数来创建一个更清晰的语法(即避免@address等初始化

注 - 因为我们使用bigmemory(因此关注RAM的使用)我有这样的例子返回只有下三角元素的N-1 X N-1矩阵,你可以修改这个,但是这是我扔在一起。

euc_dist.cpp

// To enable the functionality provided by Armadillo's various macros, 
// simply include them before you include the RcppArmadillo headers. 
#define ARMA_NO_DEBUG 

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

using namespace Rcpp; 
using namespace arma; 

// The following header file provides the definitions for the BigMatrix 
// object 
#include <bigmemory/BigMatrix.h> 

// C++11 plugin 
// [[Rcpp::plugins(cpp11)]] 

template <typename T> 
void BigArmaEuclidean(const Mat<T>& inBigMat, Mat<T> outBigMat) { 

    int W = inBigMat.n_rows; 

    for(int i = 0; i < W - 1; i++){ 
     for(int j=i+1; j < W; j++){ 
      outBigMat(j-1,i) = sqrt(sum(pow((inBigMat.row(i) - inBigMat.row(j)),2))); 
     } 
    } 
} 

// [[Rcpp::export]] 
void BigArmaEuc(SEXP pInBigMat, SEXP pOutBigMat) { 
    // First we tell Rcpp that the object we've been given is an external 
    // pointer. 
    XPtr<BigMatrix> xpMat(pInBigMat); 
    XPtr<BigMatrix> xpOutMat(pOutBigMat); 


    int type = xpMat->matrix_type(); 
    switch(type) { 
     case 1: 
     BigArmaEuclidean(
      arma::Mat<char>((char *)xpMat->matrix(), xpMat->nrow(), xpMat->ncol(), false), 
      arma::Mat<char>((char *)xpOutMat->matrix(), xpOutMat->nrow(), xpOutMat->ncol(), false) 
     ); 
     return; 

     case 2: 
     BigArmaEuclidean(
      arma::Mat<short>((short *)xpMat->matrix(), xpMat->nrow(), xpMat->ncol(), false), 
      arma::Mat<short>((short *)xpOutMat->matrix(), xpOutMat->nrow(), xpOutMat->ncol(), false) 
     ); 
     return; 

     case 4: 
     BigArmaEuclidean(
      arma::Mat<int>((int *)xpMat->matrix(), xpMat->nrow(), xpMat->ncol(), false), 
      arma::Mat<int>((int *)xpOutMat->matrix(), xpOutMat->nrow(), xpOutMat->ncol(), false) 
     ); 
     return; 

     case 8: 
     BigArmaEuclidean(
      arma::Mat<double>((double *)xpMat->matrix(), xpMat->nrow(), xpMat->ncol(), false), 
      arma::Mat<double>((double *)xpOutMat->matrix(), xpOutMat->nrow(), xpOutMat->ncol(), false) 
     ); 
     return; 

     default: 
     // We should never get here, but it resolves compiler warnings. 
     throw Rcpp::exception("Undefined type for provided big.matrix"); 
    } 

} 

我的小包装

bigMatrixEuc <- function(bigMat){ 
    zeros <- big.matrix(nrow = nrow(bigMat)-1, 
         ncol = nrow(bigMat)-1, 
         init = 0, 
         type = typeof(bigMat)) 
    BigArmaEuc([email protected], [email protected]) 
    return(zeros) 
} 

测试

library(Rcpp) 
sourceCpp("euc_dist.cpp") 

library(bigmemory) 

set.seed(123) 
mat <- matrix(rnorm(16), 4) 
bm <- as.big.matrix(mat) 

# Call new euclidean function 
bm_out <- bigMatrixEuc(bm)[] 

# pull out the matrix elements for out purposes 
distMat <- as.matrix(dist(mat)) 
distMat[upper.tri(distMat, diag=TRUE)] <- 0 
distMat <- distMat[2:4, 1:3] 

# check if identical 
all.equal(bm_out, distMat, check.attributes = FALSE) 
[1] TRUE 
+1

我运行了上面的代码,并将'bm_out'作为'matrix'。阅读包装,我曾认为'bm_out'应该是'big.matrix'。我错了吗?这个例子确实应该产生一个“矩阵”?任何直接将'bm_out'作为'big.matrix'(而不是'matrix'我将它传递给'as.big.matrix')的方法# – Ricky

+1

@Ricky只是删除括号然后'[]',因为它们只用于确认输出与'dist'相同。 – cdeterman