7

我会编写一个函数(使用)从向量Rcpp中删除所有NA值。R

在这样做之前,我通过function做了一点测试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

为什么相同的功能(显然)表现不同?这里会发生什么?

4

4 回答 4

7

跟进你的回答,我会使用这样的模板:

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. 这将导致更少的数据副本。


使用 (svn revision >= 4308) 的开发版本Rcpp,它适用于所有类型,然后我们可以使用我们的RCPP_RETURN_VECTOR调度宏而不是编写switch

// [[Rcpp::export]]
SEXP na_omit( SEXP x ){
     RCPP_RETURN_VECTOR( na_omit_template, x ) ;   
}

na_omit已包含在Rcpp(svn revision >= 4309) 中,经过一些修改,即它可以处理命名向量和任意糖表达式。

于 2013-04-13T06:11:59.387 回答
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 values,这是我最初的目的。

不幸的是,目前它不适用于所有类型的向量,如下面的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向量。

当前未解决的问题是:

  1. 我不确定为什么会出现这个错误,我想找出原因。任何想法?
  2. 如@Sameer 所示,先前的模板化函数有一个奇怪的行为。
  3. 如何接受character向量?

我想清楚了 tocase STRSXP: return na_omit_template<STRSXP, ?>(x);但是这个陈述不能代替std::string, Rcpp:Stringto ?

于 2013-04-11T19:16:39.993 回答
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>()));
}
于 2013-04-11T18:19:36.377 回答
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);
}
于 2016-05-07T13:51:46.967 回答