Извлечение столбца с NA из объекта Bigmemory в Rcpp

Я пытаюсь создать функцию, которая извлекает столбец из объекта big.matrix в Rcpp (чтобы его можно было проанализировать в cpp, прежде чем выводить результаты в R), но я не могу понять, как заставить его распознавать NA (теперь они представлены как -2147483648 - как показано в моем минимальном примере ниже). Было бы еще лучше, если бы я мог получить доступ к функции GetMatrixCols (src / bigmemory.cpp) прямо из Rcpp, но мне еще предстоит найти способ сделать это.

#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(bm@address)
bigmemory:::GetCols.bm(bm, 3)
GetColumn(bm@address, 2)
*/

2 ответа

Решение

Это здорово! Оставайтесь со мной на мгновение:

tl; dr: работает после исправления:

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(bm@address)
[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(bm@address, 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 754 имеет 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(bm@address)
bigmemory:::GetCols.bm(bm, 3)
GetColumn(bm@address, 2)
*/

Доступ к столбцу big.matrix в Rcpp это не сложно, например, вы можете получить вектор std, вектор Armadillo или вектор Eigen со следующим кодом (может существовать более чистый код):

// [[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(bm@address, 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 не знает о NA, но когда вы возвращаетесь к R, вы сохраняете их.

Таким образом, это зависит от того, какие операции вы хотите сделать в Rcpp над столбцами. Я думаю, что если вы используете операции Eigen или Armadillo напрямую, все должно быть в порядке, но вы наверняка получите много NA в своем результате.

Может быть, будет понятнее, если вы скажете, какие операции вы хотите выполнить.

Другие вопросы по тегам