Как получить имена столбцов, даже если они равны NULL в Rcpp? - PullRequest
0 голосов
/ 25 апреля 2019

Я хочу получить имена столбцов матрицы, чтобы установить другую, но если у матрицы нет имен столбцов (или задано значение NULL), следующий код завершает мой сеанс R.

CharacterVector cn = colnames(x);

Следующий код - это способ получения имен столбцов матрицы, даже если ее нет.

#include <Rcpp.h>
using namespace Rcpp;

// Get column names or empty
// [[Rcpp::export]]
CharacterVector get_colnames(const NumericMatrix &x) {
   CharacterVector cn;

   SEXP cnm = colnames(x);
   if (!Rf_isNull(cnm)) cn = cnm;

   return(cn);
}

Есть ли более элегантный способ?

Ответы [ 2 ]

3 голосов
/ 25 апреля 2019

Несколько замечаний:

  1. В матрицах не всегда установлены colnames() или rownames().
    • Если оно установлено, то объект имеет атрибут dimnames.
  2. Можно проверить наличие значения через API C для R.
    • например, Rf_isNull().
  3. Альтернативной проверкой существования может быть проверка того, является ли dimnames частью атрибутов объекта.
    • Оттуда проверьте, является ли запись в dimnames нулевой.

Давайте проверим это с первой точки, сначала создав матрицу без имен и затем создание одного с именами.Наконец, мы представим более подробную версию вашей функции, которая пытается разрешить матрицу без имен столбцов.

Матричная конструкция

Итак, традиционная матричная конструкция будетbe:

x_no_names = matrix(1:4, nrow = 2)

x_no_names
#>      [,1] [,2]
#> [1,]    1    3
#> [2,]    2    4
colnames(x_no_names)
#> NULL
rownames(x_no_names)
#> NULL
attributes(x_no_names)
#> $dim
#> [1] 2 2

Таким образом, * dimnames для созданной матрицы без имен столбцов или строк не существует.

Что произойдет, если мы присвоим имена столбцов или строкатрибуты?

# Create a matrix with names
x_named = x_no_names
colnames(x_named) = c("Col 1", "Col 2")
rownames(x_named) = c("Row 1", "Row 2")

# View attributes
attributes(x_named)
#> $dim
#> [1] 2 2
#> 
#> $dimnames
#> $dimnames[[1]]
#> [1] "Row 1" "Row 2"
#> 
#> $dimnames[[2]]
#> [1] "Col 1" "Col 2"

# View matrix object
x_named
#>       Col 1 Col 2
#> Row 1     1     3
#> Row 2     2     4

Примечание: объект matrix теперь имеет атрибут dimnames.

Реализация проверки в C ++

С нашим пониманием matrix структура, мы можем проверить:

  1. Существует ли dimnames как атрибут на матрице?
  2. Является ли вторая запись в dimnames не NULL?

Примечание. Этот подход сделает исходную функцию более многословной.Компромисс - это функция, позволяющая избежать необходимости использования SEXP возвращаемого типа.

#include <Rcpp.h>

// Get column names or empty
// [[Rcpp::export]]
Rcpp::CharacterVector get_colnames(const Rcpp::NumericMatrix &x) {

  // Construct a character vector
  Rcpp::CharacterVector cn;

  // Create a numerical index for each column
  Rcpp::IntegerVector a = Rcpp::seq_len(x.ncol());
  // Coerce it to a character
  Rcpp::CharacterVector b = Rcpp::as<Rcpp::CharacterVector>(a);

  // Assign to character vector
  cn  = b;

  if(x.hasAttribute("dimnames")) {
    Rcpp::List dimnames = x.attr( "dimnames" ) ;

    if(dimnames.size() != 2) {
      Rcpp::stop("`dimnames` attribute must have a size of 2 instead of %s.", dimnames.size());
    }

    // Verify column names exist by checking for NULL
    if(!Rf_isNull(dimnames[1]) ) {
      // Retrieve colnames and assign to cn.
      cn = dimnames[1];
    } else {
     // Assign to the matrix
     colnames(x) = cn;
    }
  } 

  return(cn);
}

Проверка варианта C ++

Вызов функции теперь даст:

get_colnames(x_no_names)
#> [1] "1" "2"

get_colnames(x_named)
#> [1] "Col 1" "Col 2"

Первое указывает, что мы используем сгенерированные индексы, тогда как второе указывает на получение значений.

2 голосов
/ 25 апреля 2019

Я начал это, а затем отвлекся.@ без покрытия покрыл это, это просто короче.

Код

#include <Rcpp.h>

// [[Rcpp::plugins(cpp11)]]
using namespace Rcpp;

// [[Rcpp::export]]
CharacterVector getColnames(const NumericMatrix &x) {
  size_t nc = x.cols();
  SEXP s = x.attr("dimnames");  // could be nil or list
  if (Rf_isNull(s)) {           // no dimnames, need to construct names
    CharacterVector res(nc);
    for (size_t i=0; i<nc; i++) {
      res[i] = std::string("V") + std::to_string(i);
    }
    return(res);
  } else {                      // have names, return colnames part
    List dn(s);
    return(dn[1]);
  }

}

/*** R
m <- matrix(1:9,3,3)
getColnames(m)
colnames(m) <- c("tic", "tac", "toe")
getColnames(m)
*/

Вывод

R> Rcpp::sourceCpp("~/git/stackoverflow/55850510/answer.cpp")

R> m <- matrix(1:9,3,3)

R> getColnames(m)
[1] "V0" "V1" "V2"

R> colnames(m) <- c("tic", "tac", "toe")

R> getColnames(m)
[1] "tic" "tac" "toe"
R>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...