2016-07-25 26 views
0

我试图创建一个从Rcpp中的big.matrix对象中提取列的函数(以便在将结果带入R之前可以在cpp中分析它) ,但我无法弄清楚如何让它识别NA(它们现在呈现为-2147483648 - 如下面我最小的例子所示)。如果我能从Rcpp直接访问函数GetMatrixColssrc/bigmemory.cpp),那就更好了,但我还没有找到一种方法来实现这一点。从Rcpp中的大内存对象中提取NA的列

#include <Rcpp.h> 
// [[Rcpp::plugins(cpp11)]] 
// [[Rcpp::depends(BH, bigmemory)]] 
#include <bigmemory/MatrixAccessor.hpp> 
#include <bigmemory/isna.hpp> 
using namespace Rcpp; 

//Logic for extracting column from a Big Matrix object 
template <typename T> 
NumericVector GetColumn_logic(XPtr<BigMatrix> pMat, MatrixAccessor<T> mat, int cn) { 
    NumericVector nv(pMat->nrow()); 
    for(int i = 0; i < pMat->nrow(); i++) { 
    if(isna(mat[cn][i])) { 
     nv[i] = NA_INTEGER; 
    } else { 
     nv[i] = mat[cn][i]; 
    } 
    } 
    return nv; 
} 

//' Extract Column from a Big Matrix. 
//' 
//' @param pBigMat A bigmemory object address. 
//' @param colNum Column Number to extract. Indexing starts from zero. 
//' @export 
// [[Rcpp::export]] 
NumericVector GetColumn(SEXP pBigMat, int colNum) { 
    XPtr<BigMatrix> xpMat(pBigMat); 

    switch(xpMat->matrix_type()) { 
    case 1: return GetColumn_logic(xpMat, MatrixAccessor<char>(*xpMat), colNum); 
    case 2: return GetColumn_logic(xpMat, MatrixAccessor<short>(*xpMat), colNum); 
    case 4: return GetColumn_logic(xpMat, MatrixAccessor<int>(*xpMat), colNum); 
    case 6: return GetColumn_logic(xpMat, MatrixAccessor<float>(*xpMat), colNum); 
    case 8: return GetColumn_logic(xpMat, MatrixAccessor<double>(*xpMat), colNum); 
    default: throw Rcpp::exception("Unknown type detected for big.matrix object!"); 
    } 
} 

/*** R 
bm <- bigmemory::as.big.matrix(as.matrix(reshape2::melt(matrix(c(1:4,NA,6:20),4,5)))) 
bigmemory:::CGetType([email protected]) 
bigmemory:::GetCols.bm(bm, 3) 
GetColumn([email protected], 2) 
*/ 

回答

2

这是一个伟大的!留在我身边了一会儿:

TL;博士:它的工作原理,一旦固定:

R> sourceCpp("/tmp/bigmemEx.cpp") 

R> bm <- bigmemory::as.big.matrix(as.matrix(reshape2::melt(matrix(c(1:4,NA,6:20),4,5)))) 

R> bigmemory:::CGetType([email protected]) 
[1] 4 

R> bigmemory:::GetCols.bm(bm, 3) 
[1] 1 2 3 4 NA 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

R> GetColumn([email protected], 2) 
[1] 1 2 3 4 NA 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
R> 

麻烦开始于内部。当你创建你的矩阵为

matrix(c(1:4,NA,6:20),4,5) 

你会得到什么?整数!

R> matrix(c(1:4,NA,6:20),4,5) 
    [,1] [,2] [,3] [,4] [,5] 
[1,] 1 NA 9 13 17 
[2,] 2 6 10 14 18 
[3,] 3 7 11 15 19 
[4,] 4 8 12 16 20 
R> class(matrix(c(1:4,NA,6:20),4,5)) 
[1] "matrix" 
R> typeof(matrix(c(1:4,NA,6:20),4,5)) 
[1] "integer" 
R> 

不是一个问题本身,但一旦你记住,IEEE 754standard具有NaN的只有浮点定义的问题(正确的,如果我错了)。

另一个问题是,你反射性地使用NumericVector在你的,但操作整数。现在R有NaN,甚至NA,对于浮点和整数,但R之外的'正常库'不会。而大设计设计代表R之外的东西,你卡住了。

该修复非常简单:使用IntegerVector(或等效地将您的整数数据转换为输入)。以下是我的代码更改版本。

// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*- 

#include <Rcpp.h> 

// [[Rcpp::plugins(cpp11)]] 
// [[Rcpp::depends(BH, bigmemory)]] 

#include <bigmemory/MatrixAccessor.hpp> 
#include <bigmemory/isna.hpp> 

using namespace Rcpp; 

//Logic for extracting column from a Big Matrix object 
template <typename T> 
IntegerVector GetColumn_logic(XPtr<BigMatrix> pMat, MatrixAccessor<T> mat, int cn) { 
    IntegerVector nv(pMat->nrow()); 
    for(int i = 0; i < pMat->nrow(); i++) { 
     if(isna(mat[cn][i])) { 
      nv[i] = NA_INTEGER; 
     } else { 
      nv[i] = mat[cn][i]; 
     } 
    } 
    return nv; 
} 

//' Extract Column from a Big Matrix. 
//' 
//' @param pBigMat A bigmemory object address. 
//' @param colNum Column Number to extract. Indexing starts from zero. 
//' @export 
// [[Rcpp::export]] 
IntegerVector GetColumn(SEXP pBigMat, int colNum) { 
    XPtr<BigMatrix> xpMat(pBigMat); 

    switch(xpMat->matrix_type()) { 
    case 1: return GetColumn_logic(xpMat, MatrixAccessor<char>(*xpMat), colNum); 
    case 2: return GetColumn_logic(xpMat, MatrixAccessor<short>(*xpMat), colNum); 
    case 4: return GetColumn_logic(xpMat, MatrixAccessor<int>(*xpMat), colNum); 
    case 6: return GetColumn_logic(xpMat, MatrixAccessor<float>(*xpMat), colNum); 
    case 8: return GetColumn_logic(xpMat, MatrixAccessor<double>(*xpMat), colNum); 
    default: throw Rcpp::exception("Unknown type detected for big.matrix object!"); 
    } 
} 

/*** R 
bm <- bigmemory::as.big.matrix(as.matrix(reshape2::melt(matrix(c(1:4,NA,6:20),4,5)))) 
bigmemory:::CGetType([email protected]) 
bigmemory:::GetCols.bm(bm, 3) 
GetColumn([email protected], 2) 
*/ 
+0

谢谢德克,非常感谢您在Rcpp上的所有工作。虽然这样可以得出正确的答案,但我仍然认为如果我可以调用GetMatrixCols函数会更容易 - 不仅因为它被设计用来完成这个确切的操作,还因为我在其他项目中有相似的需求 - > * *需要在我自己的Rcpp代码中调用Rcpp Exported函数。**我创建了一个头文件,其中包含'SEXP GetMatrixCols(SEXP bigMatAddr,SEXP col);'并构建了一个新版本的bigmemory,但是当我sourceCpp:ed:'dyn.load中的错误时..任何想法如何进行或我在做一些非常愚蠢的事情吗? – samssan

+0

Re 1):我认为那是你的错误。那么NA的二进制模式可能无法在强制拷贝从“double”到“int”的情况下存活。只需在输入中创建“双”。 Re 2)和粗体文本。不知道你在说什么。 *使用Rcpp属性的每个*包都可以做到这一点,请参阅Rcpp Atttributes小插曲了解详细信息。重新3)我怀疑你有一个挂包的构建。新问题或发布到rcpp-devel? –

+0

不知道这是否更清楚,但我会试一试。 OtherPackage中有一个cpp函数,它没有在该包的头文件中定义。我需要在cpp级别的MyPackage中使用该函数。我在这里找到了一个类似的问题[链接](http://stackoverflow.com/questions/27079811/how-do-i-share-c-functions-in-rcpp-based-libraries-between-r-packages?rq= 1)。试图暂时遵循这一点。谢谢! – samssan

1

访问在RCPP一个big.matrix的列并不困难, 可以例如得到一个std载体,犰狳载体或用下面的代码的本征矢量 (可能存在清洁器代码):

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

using namespace Rcpp; 
using namespace arma; 
using namespace Eigen; 
using namespace std; 

// [[Rcpp::plugins(cpp11)]] 

// [[Rcpp::export]] 
ListOf<IntegerVector> AccessVector(SEXP pBigMat, int j) { 
    XPtr<BigMatrix> xpMat(pBigMat); 
    MatrixAccessor<int> macc(*xpMat); 

    int n = xpMat->nrow(); 

    // Bigmemory 
    cout << "Bigmemory:"; 
    for (int i = 0; i < n; i++) { 
    cout << macc[j][i] << ' '; 
    } 
    cout << endl;  

    // STD VECTOR 
    vector<int> stdvec(macc[j], macc[j] + n); 

    // ARMA VECTOR 
    Row<int> armavec(macc[j], n); // Replace Row by Col if you want 

    // EIGEN VECTOR 
    VectorXi eigenvec(n); 
    memcpy(&(eigenvec(0)), macc[j], n * sizeof(int)); 

    return(List::create(_["Std vector"] = stdvec, 
         _["Arma vector"] = armavec, 
         _["Eigen vector"] = eigenvec)); 
} 

AccessVector([email protected], 2)让你:

Bigmemory:1 2 3 4 -2147483648 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
$`Std vector` 
[1] 1 2 3 4 NA 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

$`Arma vector` 
    [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] 
[1,] 1 2 3 4 NA 6 7 8 9 10 11 12 13 14 15 
    [,16] [,17] [,18] [,19] [,20] 
[1,] 16 17 18 19 20 

$`Eigen vector` 
[1] 1 2 3 4 NA 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

你可以看到,C不知道来港定居,但当回到R时,你保留它们。

所以,这取决于你想在列上的Rcpp中执行什么操作。我认为如果你直接使用Eigen或者Arm​​adillo操作,那应该没问题,但是你的结果肯定会得到很多的NAs。

也许它会更清晰,如果你说什么是你想要做的这些操作。