Fastest way to detect if vector has at least 1 NA?

70

What is the fastest way to detect if a vector has at least 1 NA in R? I've been using:

sum( is.na( data ) ) > 0

But that requires examining each element, coercion, and the sum function.

SFun28
quelle

Antworten:

66

As of R 3.1.0 anyNA() is the way to do this. On atomic vectors this will stop after the first NA instead of going through the entire vector as would be the case with any(is.na()). Additionally, this avoids creating an intermediate logical vector with is.na that is immediately discarded. Borrowing Joran's example:

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

Beachten Sie, dass es wesentlich schneller ist, selbst wenn wir den letzten Wert des Vektors ändern. Dies ist teilweise auf die Vermeidung des logischen Zwischenvektors zurückzuführen.

BrodieG
quelle
68

Ich denke:

any(is.na(data))

sollte etwas schneller sein.

Sacha Epskamp
quelle
1
obwohl es immer noch erforderlich ist, jedes Element zu durchlaufen. Ich frage mich, ob es eine first () - Funktion oder ähnliches gibt, die stoppt, sobald eine Bedingung erfüllt ist
SFun28
Ich bin mir nicht sicher, ich wäre nicht überrascht, wenn ich any()aufhöre, nachdem es eine FALSCHE gefunden hat. In jedem Fall ist der Moment, in any(...)dem die Verarbeitung zu langsam wird, wahrscheinlich vorbei an dem Moment, in dem Ihr RAM leer ist.
Sacha Epskamp
1
Es gibt auch die all()Funktion, die übrigens wie erwartet funktioniert. Könnte nützlich sein (nicht für dieses Problem, aber im Allgemeinen).
Sacha Epskamp
7
anyund hören allSie auf zu iterieren, wenn sie a TRUEbzw. a FALSEerreichen; siehe checkValuesin svn.r-project.org/R/trunk/src/main/logic.c ; das is.nazwingt aber immer noch alles.
Aaron verließ Stack Overflow
2
Aaron, the remaining cost is the is.na(data) which gets computed first, and for all elements of data irrespective of whether an early one is in fact NA. We do improve on that with the Rcpp sugar version of is.na() (which is implemented in C++ for use via Rcpp). See my answer for more.
Dirk Eddelbuettel
16

We mention this in some of our Rcpp presentations and actually have some benchmarks which show a pretty large gain from embedded C++ with Rcpp over the R solution because

  • a vectorised R solution still computes every single element of the vector expression

  • Wenn es Ihr Ziel ist, nur zu befriedigen any(), können Sie nach dem ersten Match abbrechen - genau das bewirkt unsere Rcpp-Zuckerlösung (im Wesentlichen: etwas C ++ - Vorlagenmagie, damit C ++ - Ausdrücke eher wie R-Ausdrücke aussehen, siehe diese Vignette für mehr) .

Wenn wir also eine kompilierte, spezialisierte Lösung zum Laufen bringen, erhalten wir tatsächlich eine schnelle Lösung. Ich sollte hinzufügen, dass ich dies zwar nicht mit den in dieser SO-Frage hier angebotenen Lösungen verglichen habe, aber hinsichtlich der Leistung einigermaßen zuversichtlich bin.

Edit And the Rcpp package contains examples in the directory sugarPerformance. It has an increase of the several thousand of the 'sugar-can-abort-soon' over 'R-computes-full-vector-expression' for any(), but I should add that that case does not involve is.na() but a simple boolean expression.

Dirk Eddelbuettel
quelle
Is there a reason why R's any computes every single element, rather than stopping at the first instance?
joran
3
R's any doesn't know what's inside it; it just evaluates whatever its argument is (all of it) and then applies any to it, which does stop at the first FALSE, but again, only after evaluating all of its argument. Dirk's Rcpp sugar version of any (as I understand it) does know how to evaluate what's inside of it term by term (at least for some expressions, anyway) so it can check each term for TRUE/FALSE as it's evaluated in turn.
Aaron left Stack Overflow
@Dirk - sehr cool. Es scheint, dass der effizienteste Weg, dies zu tun, mit eingebettetem c ++ ist ... oder ist das Betrügen, da es keine reine R-Antwort ist =) danke für den Link zu Rcpp!
SFun28
8

Man könnte eine for-Schleife schreiben, die bei NA stoppt, aber die Systemzeit hängt dann davon ab, wo sich die NA befindet ... (wenn es keine gibt, dauert es ziemlich lange)

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 
EDi
quelle
1
Tolle Idee, um mit der Nacount-Funktion zu vergleichen! Sie haben Recht, dass das Timing davon abhängt, wo sich die erste NA befindet (falls vorhanden). Ich habe Ihr Experiment wiederholt, außer dass ich eine einzelne NA am Ende des langen Vektors platziert habe. Hier sind die Ergebnisse: nacount (x) = 86,14, any (is.na (x)) = 0,4, sum (is.na (x))> 0 = 1,64. Nacount (wie erwartet) ist in diesem Fall schrecklich. Interessanter ist, wie viel besser (...) ist als Summe (...)> 0
SFun28
6

Hier sind einige aktuelle Zeiten von meiner (langsamen) Maschine für einige der verschiedenen Methoden, die bisher besprochen wurden:

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

Es ist nicht überraschend, dass matchund %in%sind ähnlich, da %in%mit implementiert wird match.

Joran
quelle
Danke, dass du das zusammengezogen hast. Ich denke, es zeigt, dass jede (...) eine fantastische, reine R-Lösung ist.
SFun28
3

Du kannst es versuchen:

d <- c(1,2,3,NA,5,3)

which(is.na(d) == TRUE, arr.ind=TRUE)
Manuel Ramón
quelle
5
Ich denke, is.na (d) == TRUE ist gleichbedeutend mit der Angabe von is.na (d)
SFun28