Переписать функцию R cummin () с использованием Rcpp и учесть NA - PullRequest
0 голосов
/ 25 августа 2018

Я учусь RCPP . В этом примере я пытаюсь свернуть мою собственную cummin() функцию, например, базовые R cummin(), за исключением того, что я хочу, чтобы моя версия имела аргумент na.rm. Это моя попытка

cummin.cpp

#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;
}

foo.R

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 - na.rm, а не narm, как я сделал. Однако, кажется, я не могу использовать точку в имени переменной c ++. Есть ли способ обойти это, чтобы я мог соответствовать соглашению R?
  2. Я не знаю заранее, будет ли пользовательский ввод числовым или целочисленным вектором, поэтому я использовал тип NumericVector в Rcpp. К сожалению, если входные данные целочисленные, выходные данные преобразуются в числовые в отличие от поведения базовых R cummin() Как люди обычно решают эту проблему?
  3. Строка if(NumericVector::is_na(x[i]) | (x[i-1] < x[i])) x[i] = x[i-1]; кажется глупой, но я не знаю лучшего способа сделать это. Предложения здесь?

1 Ответ

0 голосов
/ 25 августа 2018

Я бы использовал это:

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. Совет Джорана хорош, просто оберните это в функцию R
  2. Я использую диспетчера, как предложил Джозеф Вуд
  3. Помните, что x передается по ссылке и изменяется, если он того же типа, который вы объявили (см. эти 2 слайда )
  4. Вам нужно обработать NA, а также NaN
  5. Вы можете использовать || вместо | для оценки только первого условия, если оно истинно.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...