我会编写一个函数(使用Rcpp
),删除NA
向量中的所有R
值。
在此之前,我通过Rcpp::cppFunction
函数执行了一些测试功能。
library(inline)
cppFunction('
Vector<INTSXP> na_test(const Vector<INTSXP>& x) {
return setdiff(x, Vector<INTSXP>::create(::traits::get_na<INTSXP>()));
}
')
这是这样的:
na_test(c(1, NA, NA, 1, 2, NA))
# [1] 1 2
之后,我尝试通过C++
模板机制来概括此功能。
因此,在外部的 .cpp 文件中(通过sourceCpp
函数提供),我写道:
template <int RTYPE>
Vector<RTYPE> na_test_template(const Vector<RTYPE>& x) {
return setdiff(x, Vector<RTYPE>::create(::traits::get_na<RTYPE>()));
}
// [[Rcpp::export(na_test_cpp)]]
SEXP na_test(SEXP x) {
switch(TYPEOF(x)) {
case INTSXP:
return na_test_template<INTSXP>(x);
case REALSXP:
return na_test_template<REALSXP>(x);
}
return R_NilValue;
}
此代码编译但行为不同,我无法解释原因。
逸岸:
na_test_cpp(c(1, NA, NA, 1, 2, NA))
# [1] 2 NA NA NA 1
为什么相同的功能(显然)表现不同?这里发生了什么?
答案 0 :(得分:6)
跟进你的回答,我会用这样的模板作为模板:
template <int RTYPE>
Vector<RTYPE> na_omit_template(const Vector<RTYPE>& x) {
int n = x.size() ;
int n_out = n - sum( is_na(x) ) ;
Vector<RTYPE> out(n_out) ;
for( int i=0, j=0; i<n; i++){
if( Vector<RTYPE>::is_na( x[i] ) ) continue ;
out[j++] = x[i];
}
return out ;
}
所以我们的想法是首先计算结果的长度,然后只使用Rcpp
向量类而不是std::vector
。这将导致更少的数据副本。
使用Rcpp
的开发版本(svn revision&gt; = 4308),它适用于所有类型的我,然后我们可以使用我们的RCPP_RETURN_VECTOR
调度宏而不是编写{{1 }}:
switch
// [[Rcpp::export]]
SEXP na_omit( SEXP x ){
RCPP_RETURN_VECTOR( na_omit_template, x ) ;
}
已包含在na_omit
(svn revision&gt; = 4309)中,只需进行一些修改,即它可以处理命名向量和任意糖表达式。
答案 1 :(得分:1)
我继续研究有关模板问题的解决方案(即参见@Sameer回复)。
所以我写了另一个函数,现在模板机制正常工作。
在外部.cpp
文件中:
#include <Rcpp.h>
template <int RTYPE, class T>
Vector<RTYPE> na_omit_template(const Vector<RTYPE>& x) {
typedef typename Vector<RTYPE>::iterator rvector_it;
if (x.size() == 0) {
return x;
}
std::vector<T> out;
rvector_it it = x.begin();
for (; it != x.end(); ++it) {
if (!Vector<RTYPE>::is_na(*it)) {
out.push_back(*it);
}
}
return wrap(out);
}
// [[Rcpp::export(na_omit_cpp)]]
SEXP na_omit(SEXP x) {
switch(TYPEOF(x)) {
case INTSXP:
return na_omit_template<INTSXP, int>(x);
case REALSXP:
return na_omit_template<REALSXP, double>(x);
case LGLSXP:
return na_omit_template<LGLSXP, bool>(x);
case CPLXSXP:
return na_omit_template<CPLXSXP, Rcomplex>(x);
case RAWSXP:
return na_omit_template<RAWSXP, Rbyte>(x);
default:
stop("unsupported data type");
}
}
此功能会删除 NA
值,这是我最初的目的。
不幸的是,目前它并不适用于所有类型的矢量,如下面的R
示例所示。
library(Rcpp)
sourceCpp('file.cpp')
na_omit_cpp(as.integer(c(1, NA, NA, 1, 2, NA))) # OK
# [1] 1 1 2
na_omit_cpp(as.numeric(c(1, NA, NA, 1, 2, NA)))
# [1] 1 1 2
na_omit_cpp(c(NA, 1L, NA, 3L, NA)) # OK
# [1] 1 3
na_omit_cpp(c(NA, 2L, 1, NA)) # OK
# [1] 2 1
na_omit_cpp(c(1.0, 1.1, 2.2, NA, 3, NA, 4)) # OK
# [1] 1.0 1.1 2.2 3.0 4.0
na_omit_cpp(c(1L, NaN, NaN, 0, NA)) # OK
# [1] 1 NaN NaN 0
na_omit_cpp(c(NA, NaN, 1.0, 0.0, 2.2, NA, 3.3, NA, 4.4)) # OK
# [1] NaN 1.0 0.0 2.2 3.3 4.4
na_omit_cpp(as.logical(c(1, 0, 1, NA))) # OK
# [1] TRUE FALSE TRUE
na_omit_cpp(as.logical(c(TRUE, FALSE, NA, TRUE, NA))) # OK
# [1] TRUE FALSE TRUE
# empty vectors ?
na_omit_cpp(c(NA)) # OK
# logical(0)
na_omit_cpp(numeric(0)) # OK
# numeric(0)
na_omit_cpp(logical(0)) # OK
# logical(0)
na_omit_cpp(raw(0)) # OK
# raw(0)
na_omit_cpp(as.raw(c(40,16,NA,0,2))) # NO! (R converts it to 00)
# [1] 28 10 00 00 02
# Warning message ...
na_omit_cpp(as.complex(c(-1, 2, 1, NA, 0, NA, -1))) # NO!
# [1] -1+0i 2+0i 1+0i NA 0+0i NA -1+0i
因此,除raw
向量和complex
向量外,此函数几乎适用于所有情况。
当前未解决的问题是:
character
向量?我清楚地想到了case STRSXP: return na_omit_template<STRSXP, ?>(x);
,但这句话无效,将std::string
,Rcpp:String
替换为?
。
答案 2 :(得分:0)
模板机制似乎工作正常。
> na_test_cpp(as.numeric(c(1, NA, NA, 1, 2, NA)))
[1] 2 NA NA NA 1
> na_test_cpp(as.integer(c(1, NA, NA, 1, 2, NA)))
[1] 1 2
这段代码适用于INTSXP,但不适用于REALSXP
Vector<REALSXP> na_test_real(const Vector<REALSXP>& x) {
return setdiff(x, Vector<REALSXP>::create(::traits::get_na<REALSXP>()));
}
答案 3 :(得分:0)
一些实施:
// naive
template <int RTYPE>
Vector<RTYPE> na_omit_impl(const Vector<RTYPE>& x) {
std::size_t n = x.size();
// Estimate out length
std::size_t n_out = 0;
for(std::size_t i = 0; i < n; ++i) {
if (Vector<RTYPE>::is_na(x[i])) continue;
++n_out;
}
// exit if no NAs
if (n_out == n) return x;
// allocate vector without filling
Vector<RTYPE> res = no_init(n_out);
// fill result vector
for(std::size_t i = 0, j = 0; i < n; ++i) {
if (Vector<RTYPE>::is_na(x[i])) continue;
res[j] = x[i];
++j;
}
return res;
}
// STL algorithms
template <int RTYPE>
struct not_na {
typedef typename Vector<RTYPE>::stored_type type;
bool operator() (const type& i) {
return !Vector<RTYPE>::is_na(i);
}
};
template <int RTYPE>
Vector<RTYPE> na_omit_impl(const Vector<RTYPE>& x) {
// Estimate out length
std::size_t n_out = std::count_if(x.begin(), x.end(), not_na<RTYPE>());
// exit if no NAs
if (n_out == x.size()) return x;
// allocate vector without filling
Vector<RTYPE> res = no_init(n_out);
// fill result vector
std::copy_if(x.begin(), x.end(), res.begin(), not_na<RTYPE>());
return res;
}
// Rcpp sugar
template <class T>
T na_omit_impl(const T& x) {
return x[!is_na(x)];
}
// Rcpp sugar
template <class T>
T na_omit_impl(const T& x) {
return Rcpp::na_omit(x);
}
所有实现都适用于RCPP_RETURN_VECTOR
宏:
// [[Rcpp::export]]
RObject na_omit(RObject x){
RCPP_RETURN_VECTOR(na_omit_impl, x);
}