我想获取一个矩阵的列名以设置另一个列,但是如果矩阵没有列名(或设置为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);
}
还有更优雅的方式吗?
答案 0 :(得分:3)
一些注意事项:
colnames()
或rownames()
。
dimnames
。 Rf_isNull()
。dimnames
是否是对象属性的一部分。
dimnames
中的条目是否为空。让我们首先通过创建一个没有名字的矩阵,然后再创建一个名字为 的矩阵来验证这些点。最后,我们将介绍您的函数的更详细的版本,该版本尝试解析没有列名的矩阵。
因此,传统的矩阵构造应为:
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
属性。
通过了解matrix
结构,我们可以检查:
dimnames
是否作为属性存在于矩阵上?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);
}
现在调用该函数将给出:
get_colnames(x_no_names)
#> [1] "1" "2"
get_colnames(x_named)
#> [1] "Col 1" "Col 2"
第一个指示我们正在使用生成的索引,而第二个指示正在检索值。
答案 1 :(得分:2)
我从头开始,然后分心。 @coatless遮住了它,这只是短了。
#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>