正如我们从这个答案anyNA()中了解到的那样,当使用overany(is.na())来检测向量是否至少有一个元素时,性能会得到显着提高NA。这是有道理的,因为 的算法在找到anyNA()第一个值后停止,而必须首先运行整个向量。NAany(is.na())is.na()
相比之下,我想知道一个向量是否至少有 1 个非值NA。这意味着我正在寻找一种在第一次遇到非值后停止的实现NA。是的,我可以使用,但随后我面临着首先运行整个向量的any(!is.na())问题。is.na()
是否存在与 等效的相反性能anyNA(),即“anyNonNA()”?
我不知道是否有一个本机函数在遇到非 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 解决方案更快(至少对于大向量而言)。