Как вернуть именованный VECSXP при написании расширений R
Недавно у меня была ошибка, и когда я ее исправил, я подумал, возможно ли вернуть VECSXP (то есть тип списка R), в котором названы элементы. Этот код C++:
SEXP do_bla()
{
int prtCnt = 0;
SEXP a = PROTECT(allocMatrix(REALSXP, 5, 5));
prtCnt++;
SEXP b = PROTECT(allocVector(REALSXP, 5));
prtCnt++;
SEXP OS = PROTECT(allocVector(VECSXP, 2));
prtCnt++;
SET_VECTOR_ELT(OS, 0, a);
SET_VECTOR_ELT(OS, 1, b);
UNPROTECT(prtCnt);
return OS;
}
дал бы список двух элементов (матрица и вектор) в R:
s <- .Call("do_bla", ....)
который может быть проиндексирован следующим образом:
s[[1]]; s[[2]]
Что бы мне пришлось изменить, чтобы сделать это возможным:
s$a; s$b
Или это невозможно?
2 ответа
См. Раздел 5.9.4 " Написание расширений R". Выделите и заполните вектор имен и установите атрибут имен в списке.
library(inline)
named <- cfunction(signature(), '
/* allocate and populate list */
SEXP OS = PROTECT(allocVector(VECSXP, 2));
SET_VECTOR_ELT(OS, 0, allocMatrix(REALSXP, 5, 5));
SET_VECTOR_ELT(OS, 1, allocVector(REALSXP, 5));
/* create names */
SEXP nms = PROTECT(allocVector(STRSXP, 2));
SET_STRING_ELT(nms, 0, mkChar("foo"));
SET_STRING_ELT(nms, 1, mkChar("bar"));
/* assign names to list */
setAttrib(OS, R_NamesSymbol, nms);
/* cleanup and return */
UNPROTECT(2);
return OS;')
Возможно, существует более идиоматический способ сделать это с помощью функций API R языка C - я склонен придерживаться C++ через Rcpp, поскольку это безопаснее и более кратко - но добавление следующего должно работать:
SEXP n = PROTECT(Rf_allocVector(STRSXP, 2));
prtCnt++;
SET_STRING_ELT(n, 0, Rf_mkChar("a"));
SET_STRING_ELT(n, 1, Rf_mkChar("b"));
Rf_setAttrib(OS, R_NamesSymbol, n);
Тем не менее, я настоятельно рекомендую использовать Rcpp, потому что вы можете устранить большую часть шаблона, который поставляется с функциями C. Вот две альтернативы, а также измененная версия вашей исходной функции:
#include <Rcpp.h>
// [[Rcpp::export]]
SEXP do_bla() {
int prtCnt = 0;
SEXP a = PROTECT(Rf_allocMatrix(REALSXP, 5, 5));
prtCnt++;
SEXP b = PROTECT(Rf_allocVector(REALSXP, 5));
prtCnt++;
SEXP OS = PROTECT(Rf_allocVector(VECSXP, 2));
prtCnt++;
SET_VECTOR_ELT(OS, 0, a);
SET_VECTOR_ELT(OS, 1, b);
SEXP n = PROTECT(Rf_allocVector(STRSXP, 2));
prtCnt++;
SET_STRING_ELT(n, 0, Rf_mkChar("a"));
SET_STRING_ELT(n, 1, Rf_mkChar("b"));
Rf_setAttrib(OS, R_NamesSymbol, n);
UNPROTECT(prtCnt);
return OS;
}
// [[Rcpp::export]]
SEXP do_bla2() {
return Rcpp::List::create(
Rcpp::Named("a") = Rcpp::NumericMatrix(5, 5),
Rcpp::Named("b") = Rcpp::NumericVector(5));
}
// [[Rcpp::export]]
SEXP do_bla3() {
Rcpp::NumericMatrix m(5, 5);
Rcpp::NumericVector v(5);
Rcpp::List res = Rcpp::List::create(m, v);
res.names() = Rcpp::CharacterVector::create("a", "b");
return res;
}
Вам может понадобиться использовать mkChar
а также setAttrib
вместо Rf_mkChar
а также Rf_setAttrib
если вы используете <Rinternals.h>
непосредственно.
do_bla()
# $a
# [,1] [,2] [,3] [,4] [,5]
# [1,] 2.371515e-322 4.743030e-322 9.654277e-315 8.695555e-322 6.518868e-310
# [2,] 2.794759e-316 2.371515e-322 6.763004e-317 2.371515e-322 6.952759e-310
# [3,] 3.458460e-323 2.797257e-316 1.630417e-322 2.852530e-316 1.630417e-322
# [4,] 6.441834e+170 1.976263e-323 4.092581e-316 1.976263e-323 4.125824e-316
# [5,] 1.818440e-306 6.952931e-310 4.008825e-316 2.121996e-314 2.154669e-316
#
# $b
# [1] 2.144130e-316 2.168146e-316 3.468674e-316 2.155101e-316 2.172224e-316
#
do_bla2()
# $a
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 0 0 0 0
# [2,] 0 0 0 0 0
# [3,] 0 0 0 0 0
# [4,] 0 0 0 0 0
# [5,] 0 0 0 0 0
#
# $b
#[1] 0 0 0 0 0
do_bla3()
# $a
# [,1] [,2] [,3] [,4] [,5]
# [1,] 0 0 0 0 0
# [2,] 0 0 0 0 0
# [3,] 0 0 0 0 0
# [4,] 0 0 0 0 0
# [5,] 0 0 0 0 0
#
# $b
#[1] 0 0 0 0 0