即使在Rcpp中它是NULL,如何获取列名?

问题描述 投票:0回答:2

我想获取矩阵的列名来设置另一个,但如果矩阵没有列名(或设置为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);
}

有更优雅的方式吗?

r rcpp
2个回答
2
投票

我开始这个然后分心了。 @coatless覆盖它,这只是更短。

Code

#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)
*/

Output

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>

3
投票

几点说明:

  1. 矩阵并不总是有colnames()rownames()集。 如果设置了一个,则该对象具有dimnames属性。
  2. 可以通过C API为R检查是否存在值。 例如Rf_isNull()
  3. 另一种存在性检查是验证dimnames是否是对象属性的一部分。 从那里,检查dimnames中的条目是否为空。

让我们通过首先创建一个没有名称的矩阵,然后创建一个带有名称的矩阵来验证这些第一点。最后,我们将介绍一个更详细的函数版本,它试图解析没有列名的矩阵。

Matrix construction

因此,传统的矩阵构造将是:

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属性。

Implementing a Check in 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);
}

Testing the C++ variant

调用该函数现在将给出:

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

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

第一个表示我们正在使用生成的索引,而第二个表示正在检索值。

© www.soinside.com 2019 - 2024. All rights reserved.