【问题标题】:Templated Rcpp function to erase NA values用于擦除 NA 值的模板化 Rcpp 函数
【发布时间】:2013-04-11 16:12:09
【问题描述】:

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

在这样做之前,我通过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

为什么相同的函数(显然)表现不同?这里发生了什么?

【问题讨论】:

  • 您的示例调用了 na_test_cpp,您的代码没有显示。
  • 这个表达式// [[Rcpp::export(na_test_cpp)]]不需要导出函数(用想要的名字)?
  • 我的错,忽略了它

标签: r templates rcpp


【解决方案1】:

根据您的回答,我会使用类似这样的模板:

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 修订版>= 4308),它适用于所有类型,然后我们可以使用我们的RCPP_RETURN_VECTOR 调度宏而不是编写switch

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

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

【讨论】:

  • 非常优雅的解决方案! ;) 任何想法让它与 STRSXP 和 CPLXSXP 向量一起工作?
  • STRSXP 工作。复杂版本不起作用,因为Rcpp 中存在错误,现已在修订版 4308 中修复。
  • 我已将na_omit 添加到Rcpp,并进行了一些修改,例如它可以处理命名向量和任意糖表达式。
  • 非常好!由于我希望使用您添加到Rcppna_omit:什么时候新发布? :) 或者我可以从你的 SVN 安装它?
  • 现在计划发布新版本。我们不会为每个新功能或错误修复发布。只需从 r-forge 获取源代码并自行构建:svn checkout svn://svn.r-forge.r-project.org/svnroot/rcpp/pkg/Rcpp
【解决方案2】:

我一直在研究有关模板问题的解决方案(即参见@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 向量之外,此函数几乎适用于所有情况。

当前未解决的问题是:

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

我对@9​​87654330@ 想得很清楚,但是将std::stringRcpp:String 替换为?,此语句不起作用。

【讨论】:

  • 我稍后会做一个实际的回复。在原始向量的情况下,没有 NA 之类的东西。
  • @RomainFrancois 你是对的,我应该知道 (dirk.eddelbuettel.com/code/rcpp/html/get__na_8h.html)。
  • 无论如何,我仍在尝试使此功能适用于字符向量。目前:没有进展。
  • case STRSXP: return na_omit_template&lt;STRSXP, String&gt;(x); 工作。
【解决方案3】:

模板机制似乎工作正常。

> 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>()));
}

【讨论】:

  • 好点!我开始认为这也可能是与setdiff 函数相关的问题,但我不确定。这就是我写在这里的原因,希望一些大师帮助我:)
  • 那是因为&gt; evalCpp( "NA_REAL == NA_REAL" ) 给了[1] FALSE。你不能用数字 NA 测试是否相等,你必须使用 is_na
【解决方案4】:

一些实现:

// 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);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-07
    • 2016-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多