【发布时间】:2011-09-26 23:11:55
【问题描述】:
检测向量是否在 R 中至少有 1 个NA 的最快方法是什么?我一直在使用:
sum( is.na( data ) ) > 0
但这需要检查每个元素、强制和求和函数。
【问题讨论】:
检测向量是否在 R 中至少有 1 个NA 的最快方法是什么?我一直在使用:
sum( is.na( data ) ) > 0
但这需要检查每个元素、强制和求和函数。
【问题讨论】:
我在想:
any(is.na(data))
应该稍微快一点。
【讨论】:
any() 在发现 FALSE 后停止,我不会感到惊讶。无论如何,any(...) 变得太慢而无法处理的那一刻可能已经过了你的 RAM 用完的那一刻。
all() 函数按预期工作。可能有用(不适用于这个问题,但一般来说)。
any 和 all 分别在达到 TRUE 或 FALSE 时停止迭代;见checkValues 在svn.r-project.org/R/trunk/src/main/logic.c ; is.na 仍然强制一切。
is.na(data),并且对于数据的所有元素,无论早期的元素是否实际上是NA。我们确实使用is.na() 的 Rcpp 糖版本(在 C++ 中实现以通过 Rcpp 使用)对此进行了改进。有关更多信息,请参阅我的答案。
从 R 3.1.0 开始,anyNA() 是执行此操作的方法。在原子向量上,这将在第一个 NA 之后停止,而不是像 any(is.na()) 那样遍历整个向量。此外,这避免了使用is.na 创建一个立即丢弃的中间逻辑向量。借用 Joran 的例子:
x <- y <- runif(1e7)
x[1e4] <- NA
y[1e7] <- NA
microbenchmark::microbenchmark(any(is.na(x)), anyNA(x), any(is.na(y)), anyNA(y), times=10)
# Unit: microseconds
# expr min lq mean median uq
# any(is.na(x)) 13444.674 13509.454 21191.9025 13639.3065 13917.592
# anyNA(x) 6.840 13.187 13.5283 14.1705 14.774
# any(is.na(y)) 165030.942 168258.159 178954.6499 169966.1440 197591.168
# anyNA(y) 7193.784 7285.107 7694.1785 7497.9265 7865.064
请注意,即使我们修改了向量的最后一个值,它也明显更快;这部分是因为避免了中间逻辑向量。
【讨论】:
我们在Rcpp 的一些演示文稿中提到了这一点,并且实际上有一些基准测试表明,嵌入式 C++ 与 Rcpp 相比 R 解决方案获得了相当巨大的收益,因为
矢量化 R 解决方案仍然计算矢量表达式的每一个元素
如果您的目标只是满足any(),那么您可以在第一场比赛后中止——这就是我们的 Rcpp 糖(本质上是:制作一些 C++ 模板魔法C++ 表达式看起来更像 R 表达式,请参阅 this vignette 了解更多信息)解决方案确实如此。
因此,通过编译专门的解决方案来工作,我们确实得到了一个快速的解决方案。我应该补充一点,虽然我没有将其与此处 SO 问题中提供的解决方案进行比较,但我对性能有相当的信心。
编辑 并且 Rcpp 包包含目录sugarPerformance 中的示例。对于any(),它比'R-computes-full-vector-expression'增加了数千个'sugar-can-abort-soon',但我应该补充一点,这种情况不涉及is.na(),但是一个简单的布尔表达式。
【讨论】:
any 计算每一个元素,而不是在第一个实例上停止,有什么原因吗?
any 不知道里面有什么;它只是评估它的论点(全部),然后将any 应用于它,它确实在第一个FALSE 处停止,但同样,只有在评估其所有论点之后。 Dirk 的 any 的 Rcpp 糖版本(据我所知)确实知道如何逐项评估其中的内容(至少对于某些表达式,无论如何),因此它可以检查每个术语的 TRUE/FALSE,因为它是依次评估的.
可以编写一个在 NA 处停止的 for 循环,但 system.time 取决于 NA 的位置...(如果没有,则需要很长时间)
set.seed(1234)
x <- sample(c(1:5, NA), 100000000, replace = TRUE)
nacount <- function(x){
for(i in 1:length(x)){
if(is.na(x[i])) {
print(TRUE)
break}
}}
system.time(
nacount(x)
)
[1] TRUE
User System verstrichen
0.14 0.04 0.18
system.time(
any(is.na(x))
)
User System verstrichen
0.28 0.08 0.37
system.time(
sum(is.na(x)) > 0
)
User System verstrichen
0.45 0.07 0.53
【讨论】:
以下是我的(慢速)机器上目前讨论的各种方法的一些实际时间:
x <- runif(1e7)
x[1e4] <- NA
system.time(sum(is.na(x)) > 0)
> system.time(sum(is.na(x)) > 0)
user system elapsed
0.065 0.001 0.065
system.time(any(is.na(x)))
> system.time(any(is.na(x)))
user system elapsed
0.035 0.000 0.034
system.time(match(NA,x))
> system.time(match(NA,x))
user system elapsed
1.824 0.112 1.918
system.time(NA %in% x)
> system.time(NA %in% x)
user system elapsed
1.828 0.115 1.925
system.time(which(is.na(x) == TRUE))
> system.time(which(is.na(x) == TRUE))
user system elapsed
0.099 0.029 0.127
match 和 %in% 相似并不奇怪,因为 %in% 是使用 match 实现的。
【讨论】:
你可以试试:
d <- c(1,2,3,NA,5,3)
which(is.na(d) == TRUE, arr.ind=TRUE)
【讨论】: