检测向量是否至少有一个非 NA 元素的最快方法是什么?(即与 `base::anyNA()` 相反)

Emm*_*man 5 r na

正如我们从这个答案anyNA()中了解到的那样,当使用overany(is.na())来检测向量是否至少有一个元素时,性能会得到显着提高NA。这是有道理的,因为 的算法在找到anyNA()第一个值后停止,而必须首先运行整个向量。NAany(is.na())is.na()

相比之下,我想知道一个向量是否至少有 1 个NA。这意味着我正在寻找一种在第一次遇到非值后停止的实现NA。是的,我可以使用,但随后我面临着首先运行整个向量的any(!is.na())问题。is.na()

是否存在与 等效的相反性能anyNA(),即“anyNonNA()”?

All*_*ron 5

我不知道是否有一个本机函数在遇到非 NA 值时会停止,但我们可以使用 Rcpp 编写一个简单的函数:

Rcpp::cppFunction("bool any_NonNA(NumericVector v) {
  for(size_t i = 0; i < v.length(); i++) {
   if(!(Rcpp::traits::is_na<REALSXP>(v[i]))) return true;
  }
  return false;
}")
Run Code Online (Sandbox Code Playgroud)

这将创建一个名为 R 的函数any_NonNA,它可以满足我们的需要。让我们在包含 100,000 个 NA 值的大向量上测试它:

test <- rep(NA, 1e5)

any_NonNA(test)
#> [1] FALSE

any(!is.na(test))
#> [1] FALSE
Run Code Online (Sandbox Code Playgroud)

现在让我们将第一个元素设置为非 NA:

test[1] <- 1

any_NonNA(test)
#> [1] TRUE

any(!is.na(test))
#> [1] TRUE
Run Code Online (Sandbox Code Playgroud)

所以它给出了正确的结果,但是速度更快吗?

当然,在这个例子中,由于它应该在第一个元素之后停止,所以它应该快得多。如果我们进行正面比较的话,确实是这样:

microbenchmark::microbenchmark(
  baseR = any(!is.na(test)),
  Rcpp  = any_NonNA(test)
)
#> Unit: microseconds
#> expr   min    lq    mean median    uq     max neval cld
#> baseR 275.1 525.0 670.948 533.05 568.7 13029.9   100   b
#> Rcpp   1.6   2.1   4.319   3.30   5.1    33.7   100  a 
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,速度快了几个数量级。如果我们的第一个非 NA 值位于向量的中间怎么办?

test[1] <- NA
test[50000] <- 1

microbenchmark::microbenchmark(
  baseR = any(!is.na(test)),
  Rcpp  = any_NonNA(test)
)
#> Unit: microseconds
#> expr   min     lq    mean median     uq     max neval cld
#> baseR 332.1 579.35 810.948 597.95 624.40 12010.4   100   b
#> Rcpp 299.4 300.70 311.516 305.10 309.25   370.1   100  a 
Run Code Online (Sandbox Code Playgroud)

还是更快,但也快不了多少。

如果我们将非 NA 值放在最后,我们应该不会看到太大的差异:

test[1] <- NA
test[50000] <- 1

microbenchmark::microbenchmark(
  baseR = any(!is.na(test)),
  Rcpp  = any_NonNA(test)
)
#> Unit: microseconds
#> expr   min     lq    mean median     uq     max neval cld
#> baseR 332.1 579.35 810.948 597.95 624.40 12010.4   100   b
#> Rcpp 299.4 300.70 311.516 305.10 309.25   370.1   100  a 
Run Code Online (Sandbox Code Playgroud)

因此,这确实看起来比基本 R 解决方案更快(至少对于大向量而言)。