【问题标题】:Calculate Euclidean distance matrix using a big.matrix object使用 big.matrix 对象计算欧几里得距离矩阵
【发布时间】:2015-01-13 12:51:42
【问题描述】:

我在R 中有一个类big.matrix 的对象,维度为778844 x 2。这些值都是整数(公里)。我的目标是使用big.matrix 计算欧几里得距离矩阵,并因此得到big.matrix 类的对象。我想知道是否有最佳方法。

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

编辑

bigmemory 包的作者和维护者 John W. Emerson 给出了以下答案:

您可以使用我期望的大代数,但这对于通过 sourceCpp() 的 Rcpp 来说也是一个非常好的用例,而且非常简短和容易。但简而言之,我们甚至不尝试提供高级功能(除了我们作为概念验证实现的基础功能)。一旦你开始谈论内存不足,没有一种算法可以涵盖所有用例。

【问题讨论】:

    标签: r matrix bigdata sparse-matrix r-bigmemory


    【解决方案1】:

    这是一种使用RcppArmadillo 的方法。其中大部分与RcppGallery example 非常相似。这将返回一个 big.matrix 以及相关的成对(按行)欧几里得距离。我喜欢将我的 big.matrix 函数包装在一个包装函数中以创建更简洁的语法(即避免 @address 和其他初始化。

    注意 - 因为我们使用的是大内存(因此关注 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(bigMat@address, zeros@address)
        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
    

    【讨论】:

    • 我运行了上面的代码,得到了bm_out 作为matrix。阅读包装,我认为bm_out 应该是big.matrix。我错了吗,这个例子确实应该产生matrix?将bm_out 直接作为big.matrix 的任何方式(而不是我传递给as.big.matrixmatrix
    • @Ricky 只需删除括号然后 '[]' 因为它们仅用于确认输出与来自 'dist' 的输出相同。
    猜你喜欢
    • 2020-06-16
    • 2014-05-08
    • 2016-08-02
    • 2012-06-27
    • 2017-01-05
    • 2018-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多