Rcpp 中如何使用 noNA?

How is noNA used in Rcpp?

Hadley Wickham 在他的 "Advanced R" 书中说:“noNA(x) 断言向量 x 不包含任何缺失值。”但是我仍然不知道如何使用它。我做不到

if (noNA(x))
    do this

那么我应该如何使用它呢?

http://adv-r.had.co.nz/Rcpp.html#rcpp-sugar

许多 Rcpp 糖表达式是通过模板 classes 实现的,这些模板具有针对输入对象 已知 没有缺失值的情况的专门化,从而允许底层算法避免执行处理 NA 值的额外工作(例如调用 is_na)。这是唯一可能的,因为 VectorBase class 有一个布尔参数,指示基础对象 是否可以 (可以,但不一定 确实 ) 是否有 NA 值。

noNA returns (when called on a VectorBase object) an instance of the Nona 模板 class。请注意 Nona 本身派生自

Rcpp::VectorBase<RTYPE, false, Nona<RTYPE,NA,VECTOR>>
//                      ^^^^^

意味着返回的对象使用本质上说 "you can assume that this data is free of NA values" 的信息进行编码。

例如,Rcpp::sum 是通过 Rcpp::sugar 命名空间中的 Sum class 实现的。在 default case 中,我们看到有额外的工作来管理缺失值的可能性:

STORAGE get() const {
    STORAGE result = 0 ;
    R_xlen_t n = object.size() ;
    STORAGE current ;
    for( R_xlen_t i=0; i<n; i++){
        current = object[i] ;
        if( Rcpp::traits::is_na<RTYPE>(current) )   // here
            return Rcpp::traits::get_na<RTYPE>() ;  // here
        result += current ;
    }
    return result ;
}

另一方面,对于输入没有缺失值的情况,也有a specialization,算法做的工作较少:

STORAGE get() const {
    STORAGE result = 0 ;
    R_xlen_t n = object.size() ;
    for( R_xlen_t i=0; i<n; i++){
        result += object[i] ;
    }
    return result ;
}

为了回答您 "how do I apply this in practice?" 的问题,这里有一个例子:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
int Sum(IntegerVector x) {
    return sum(x);
}

// [[Rcpp::export]]
int SumNoNA(IntegerVector x) {
    return sum(noNA(x));
}

对这两个函数进行基准测试,

set.seed(123)
x <- as.integer(rpois(1e6, 25))

all.equal(Sum(x), SumNoNA(x))
# [1] TRUE

microbenchmark::microbenchmark(
    Sum(x), 
    SumNoNA(x),
    times = 500L
)
# Unit: microseconds
#        expr     min      lq     mean   median       uq      max neval
#      Sum(x) 577.386 664.620 701.2422 677.1640 731.7090 1214.447   500
#  SumNoNA(x) 454.990 517.709 556.5783 535.1935 582.7065 1138.426   500

noNA版本确实更快。