Проверка Null и NA вектора в Rcpp - PullRequest
2 голосов
/ 06 марта 2020

Я пытаюсь оценить сумму вектора (y) в зависимости от того, равно ли значение второго обнуляемого вектора (r) NA или нет. Если второй вектор r равен NULL, все значения y должны суммироваться. Если все элементы r равны NA, функция должна вернуть NA. Пожалуйста, смотрите конец текста для желаемого результата.

Сначала я попробовал следующий код:

library(Rcpp)
cppFunction('double foo(NumericVector y, Rcpp::Nullable<Rcpp::IntegerVector> r = R_NilValue) {
  double output = 0;
  bool return_na = !Rf_isNull(r);
  int y_count = y.size();
  for (int i = 0; i < y_count; i++) {
    if (Rf_isNull(r)  || !R_IsNA(r[i])) {
    //// if (Rf_isNull(r)  || !R_IsNA(as<IntegerVector>(r)[i])) {
      if (!Rf_isNull(r))
        Rcout << R_IsNA(as<IntegerVector>(r)[i]) << " - "<< as<IntegerVector>(r)[i] << std::endl;
      output = output + y[i];
      return_na = false;
    } 
  }
  if (return_na) 
    return NA_REAL;
  return output;
}')

Это дало мне следующую ошибку:

 error: invalid use of incomplete type 'struct SEXPREC'
     if (Rf_isNull(r)  || !R_IsNA(r[i])) {
                                     ^

Чтобы решить эту проблему, я использовал if (Rf_isNull(r) || !R_IsNA(as<IntegerVector>(r)[i])) { вместо , Но на этот раз при преобразовании в целочисленный вектор значения NA преобразуются в число, а проверка R_IsNA() дает ложное срабатывание.

Вот ожидаемый результат, который я хочу.

foo(1:4, NULL) #  <- This should return 10 = 1 + 2 + 3 + 4
foo(1:4, c(1, 1, 1, 1)) #  <- This should return 10 = 1 + 2 + 3 + 4
foo(1:4, c(1, 1, NA, 1)) #  <- This should return 7 = 1 + 2 + 4
foo(1:4, c(NA, NA, NA, NA)) # <- This should return NA

Как получить нужную функцию? (Этот пример упрощен, меня не особенно интересует функция суммирования. Вместо этого меня интересует проверка NA и NULL одновременно, как указано в примере.)

1 Ответ

6 голосов
/ 07 марта 2020

Три предложения:

  • Используйте R cpp вместо R's C API.
  • Вернитесь рано, когда r равно NULL.
  • Создайте LogicalVector перед циклом по входному вектору.
#include <Rcpp.h>

// [[Rcpp::export]]
double foo(Rcpp::NumericVector y, Rcpp::Nullable<Rcpp::IntegerVector> r = R_NilValue) {
    if (r.isNull())
        return Rcpp::sum(y);

    Rcpp::LogicalVector mask = Rcpp::is_na(r.as());
    if (Rcpp::is_true(Rcpp::all(mask))) 
        return NA_REAL;

    double output = 0.0;
    int y_count = y.size();
    for (int i = 0; i < y_count; ++i) {
        if (!mask[i]) {
            output += y[i];
        } 
    }
    return output;
}

/***R
foo(1:4, NULL) #  <- This should return 10 = 1 + 2 + 3 + 4
foo(1:4, c(1, 1, 1, 1)) #  <- This should return 10 = 1 + 2 + 3 + 4
foo(1:4, c(1, 1, NA, 1)) #  <- This should return 7 = 1 + 2 + 4
foo(1:4, c(NA, NA, NA, NA)) # <- This should return NA
*/ 

Результат:

> Rcpp::sourceCpp('60569482.cpp')

> foo(1:4, NULL) #  <- This should return 10 = 1 + 2 + 3 + 4
[1] 10

> foo(1:4, c(1, 1, 1, 1)) #  <- This should return 10 = 1 + 2 + 3 + 4
[1] 10

> foo(1:4, c(1, 1, NA, 1)) #  <- This should return 7 = 1 + 2 + 4
[1] 7

> foo(1:4, c(NA, NA, NA, NA)) # <- This should return NA
[1] NA

Дальнейшее предложение:

  • Используйте маска для поднабора y.
#include <Rcpp.h>

// [[Rcpp::export]]
double foo(Rcpp::NumericVector y, Rcpp::Nullable<Rcpp::IntegerVector> r = R_NilValue) {
    if (r.isNull())
        return Rcpp::sum(y);

    Rcpp::LogicalVector mask = Rcpp::is_na(r.as());
    if (Rcpp::is_true(Rcpp::all(mask))) 
        return NA_REAL;

    Rcpp::NumericVector tmp = y[!mask];
    return Rcpp::sum(tmp);
}
...