代码之家  ›  专栏  ›  技术社区  ›  Ben

使用Rcpp重写R的cummin()函数并允许NAs

  •  6
  • Ben  · 技术社区  · 5 年前

    我在学习 Rcpp . 在这个例子中,我试图把我自己的 cummin() ,但我希望我的版本 na.rm 争论。这是我的尝试

    #include <Rcpp.h>
    using namespace Rcpp;
    
    // [[Rcpp::export]]
    NumericVector cummin_cpp(NumericVector x, bool narm = false){
      // Given a numeric vector x, returns a vector of the 
      // same length representing the cumulative minimum value
      // if narm = true, NAs will be ignored (The result may 
      // contain NAs if the first values of x are NA.)
      // if narm = false, the resulting vector will return the 
      // cumulative min until the 1st NA value is encountered
      // at which point all subsequent entries will be NA
    
      if(narm){
        // Ignore NAs
        for(int i = 1; i < x.size(); i++){
          if(NumericVector::is_na(x[i]) | (x[i-1] < x[i])) x[i] = x[i-1];
        }
      } else{
        // Don't ignore NAs
        for(int i = 1; i < x.size(); i++){
          if(NumericVector::is_na(x[i-1]) | NumericVector::is_na(x[i])){
            x[i] = NA_REAL;
          } else if(x[i-1] < x[i]){
            x[i] = x[i-1];
          }
        }
      }
    
      return x;
    }
    

    福勒

    library(Rcpp)
    sourceCpp("cummin.cpp")
    
    x <- c(3L, 1L, 2L)
    cummin(x)  # 3 1 1
    cummin_cpp(x)  # 3 1 1
    
    class(cummin(x))  # integer
    class(cummin_cpp(x))  # numeric
    

    我有几个问题。。

    1. R的标准变量名是 ,不是 narm 就像我做的那样。但是,似乎我不能在c++变量名中使用点。有没有办法让我和R的惯例保持一致?
    2. 我不知道用户的输入是数值向量还是整数向量,所以我使用了Rcpp的numeric vector类型。不幸的是,如果输入是整数,则输出将转换为数字,而不是基R cummin() 行为。人们通常如何处理这个问题?
    3. if(NumericVector::is_na(x[i]) | (x[i-1] < x[i])) x[i] = x[i-1]; 看起来很傻,但我不知道更好的办法。这里有什么建议?
    1 回复  |  直到 5 年前
        1
  •  5
  •   F. Privé    5 年前

    template<typename T, int RTYPE>
    Vector<RTYPE> cummin_cpp2(Vector<RTYPE> x, bool narm){
    
      Vector<RTYPE> res = clone(x);
      int i = 1, n = res.size();
      T na;
    
      if(narm){
        // Ignore NAs
        for(; i < n; i++){
          if(ISNAN(res[i]) || (res[i-1] < res[i])) res[i] = res[i-1];
        }
      } else{
        // Do not ignore NAs
        for(; i < n; i++){
          if(ISNAN(res[i-1])) {
            na = res[i-1];
            break;
          } else if(res[i-1] < res[i]){
            res[i] = res[i-1];
          }
        }
        for(; i < n; i++){
          res[i] = na;
        }
      }
    
      return res;
    }
    
    
    // [[Rcpp::export]]
    SEXP cummin_cpp2(SEXP x, bool narm = false) {
      switch (TYPEOF(x)) {
      case INTSXP:  return cummin_cpp2<int, INTSXP>(x, narm);
      case REALSXP: return cummin_cpp2<double, REALSXP>(x, narm);
      default: Rcpp::stop("SEXP Type Not Supported."); 
      }
    }
    

    试试这个:

    x <- c(NA, 7, 5, 4, NA, 2, 4)
    x2 <- as.integer(x)
    
    cummin_cpp(x, narm = TRUE)
    x
    
    cummin_cpp(x2)
    x2
    
    
    x <- c(NA, 7, 5, 4, NA, 2, 4)
    x2 <- as.integer(x)
    x3 <- replace(x, is.na(x), NaN)
    
    cummin_cpp2(x, narm = TRUE)
    x
    
    cummin_cpp2(x2)
    x2
    
    cummin_cpp2(x3)
    x3
    

    说明:

    1. Joran的建议很好,把它包装成R函数就行了
    2. 小心点 x 是通过引用传递的,如果与您声明的类型相同,则会对其进行修改(请参见 these 2 slides
    3. 你需要处理 NA 以及 NaN
    4. 你可以用 || 而不是 |