检测向量是否至少具有1 NA的最快方法?


70

检测向量NA的R值是否至少为1的最快方法是什么?我一直在使用:

sum( is.na( data ) ) > 0

但这需要检查每个元素,强制和求和函数。

Answers:


66

从R 3.1.0开始anyNA()是执行此操作的方法。在原子向量上,它将在第一个NA之后停止,而不是像那样经过整个向量any(is.na())。另外,这避免了创建中间逻辑向量,is.na该中间逻辑向量被立即丢弃。借用乔兰的例子:

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

请注意,即使我们修改了向量的最后一个值,它的速度也显着提高。这部分是由于避免了中间逻辑向量。


68

我在想:

any(is.na(data))

应该稍微快一点。


1
尽管它仍然需要遍历每个元素。想知道是否存在first()函数或类似的函数在满足条件后停止运行
SFun28'1

不知道,如果any()发现FALSE后停止,我不会感到惊讶。无论如何,any(...)变得太慢以至于无法处理的那一刻可能已经超过了RAM耗尽的那一刻。
萨莎·埃斯普坎普

1
all()顺便说一句,还有功能可以正常使用。可能很有用(不是针对此问题,而是一般而言)。
2011年

7
anyall在到达aTRUE或a时停止迭代FALSE;看到checkValuessvn.r-project.org/R/trunk/src/main/logic.c ; 在is.na仍然强求一切虽然。
亚伦(Aaron)

2
亚伦(Aaron),剩余成本是is.na(data)首先计算出来的成本,对于所有数据元素,无论早期是否事实上是NA。我们使用Rcpp糖版本is.na()(在C ++中实现,可通过Rcpp使用)进行了改进。看到我的答案更多。
Dirk Eddelbuettel 2011年

16

我们在一些Rcpp演示文稿中提到了这一点,实际上还有一些基准测试,这些基准显示出与R解决方案相比,使用Rcpp的嵌入式C ++的收益很大

  • 向量化的R解决方案仍会计算向量表达式的每个元素

  • 如果您的目标是仅仅满足any(),那么您可以在第一个匹配项后中止-这就是我们的Rcpp糖(本质上:使C ++表达式看起来更像R表达式的一些C ++模板魔术,更多信息请参见此插图) 。

因此,通过获得已编译的专用解决方案来工作,我们确实获得了快速的解决方案。我应该补充一点,虽然我没有将其与此处的SO问题所提供的解决方案进行比较,但我对性能相当有信心。

编辑并且Rcpp软件包在目录中包含示例sugarPerformance。它比'R-computes-full-vector-expression'的'sugar-can-abort-soon'增加了几千any(),但我应该补充一点,这种情况只涉及is.na()一个简单的布尔表达式。


有一个理由为什么R会any计算每个元素,而不是首先停止计算?
joran 2011年

3
R'sany不知道里面有什么。它只是评估其参数(所有参数),然后对其应用any,它确实在第一个停止FALSE,但仅在评估其所有参数之后才停止。Dirk的Rcpp糖版本any(据我所知)确实知道如何按术语(至少对于某些表达式而言)来评估术语中的内容,因此它可以依次评估每个术语是否为TRUE / FALSE。
亚伦(Aaron)

@Dirk-非常酷。似乎最有效的方法是使用嵌入式c ++ ...或者是作弊,因为它不是纯R答案=)感谢Rcpp的链接!
SFun28

8

可以编写一个for循环在NA处停止,但是system.time取决于NA所在的位置...(如果不存在,则需要looooong)

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 

1
对nacount函数进行基准测试的好主意!没错,时间取决于第一个NA(如果有)在哪里。我重复了您的实验,只是我在长向量的末尾放置了一个NA。结果是:nacount(x)= 86.14,any(is.na(x))= 0.4,sum(is.na(x))> 0 = 1.64。在这种情况下,nacount(如预期的那样)是可怕的。更有意思的是,any(...)比sum(...)> 0
好得多

6

这是我(慢速)机器上实际的时间,用于到目前为止讨论的各种方法:

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


感谢您将它们组合在一起。我认为这表明any(...)是一种出色的纯R解决方案。
SFun28

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.