Hoffmann
Hoffmann

Reputation: 1082

NA-recognizing boolean operator

Is there a boolean opperator that gives me NA if any of the parts is NA? Currently

NA & FALSE == FALSE
FALSE & NA == FALSE
NA & TRUE  == NA
TRUE & NA  == NA

I'd like to have:

NA x FALSE == NA
FALSE x NA == NA

PS:

I'm searching for an operator x for which

  x     |  a=TRUE  |  =FALSE | =NA
-----------------------------------
b=TRUE  |    TRUE  |   FALSE |  NA
 =FALSE |    FALSE |   FALSE |  NA
 =NA    |    NA    |   NA    |  NA

so I could do

result <- a x b

Upvotes: 7

Views: 377

Answers (2)

Ferdinand.kraft
Ferdinand.kraft

Reputation: 12819

Dason's approach is cool, but if want something more readable by mortals, use this:

`%&%` <- function(e1, e2) ifelse(is.na(e1)|is.na(e2), NA, e1 & e2)

Results:

> x <- c(TRUE, FALSE, NA)
> outer(x, x, `%&%`)
      [,1]  [,2] [,3]
[1,]  TRUE FALSE   NA
[2,] FALSE FALSE   NA
[3,]    NA    NA   NA

EDIT: Benchmarking:

It should be noted that Dason's approach is the fastest, as shown below:

library(microbenchmark)
library(compiler)

x <- sample(c(TRUE, FALSE, NA), size=1e3, TRUE)
y <- sample(c(TRUE, FALSE, NA), size=1e3, TRUE)

`%&1%` <- function(e1, e2) ifelse(is.na(e1)|is.na(e2), NA, e1 & e2)
`%&2%` <- function(x, y) (!x | y) & (x | y) & (x | !y)
`%and%` <- function(x, y)as.logical(x*y)

Note: %&2% is a funny alternative I've just found by luck :-)

Let's compile all this to see what happen:

`%C&2%` <- cmpfun(`%&2%`)
`%C&1%` <- cmpfun(`%&1%`)
`%Cand%` <- cmpfun(`%and%`)

> microbenchmark(x %&1% y, x %&2% y, x %and% y, x %C&1% y, x %C&2% y, x %Cand% y, times=1000)
Unit: microseconds
       expr     min      lq  median      uq      max neval
   x %&1% y 201.575 206.124 208.574 211.024 1822.917  1000
   x %&2% y  86.439  87.140  87.839  88.190 1244.441  1000
  x %and% y  13.299  13.999  14.349  14.700 1141.905  1000
  x %C&1% y 200.525 205.775 208.574 210.674 1554.151  1000
  x %C&2% y  84.690  85.390  86.090  86.440 1212.596  1000
 x %Cand% y  13.299  13.649  14.349  14.699 1141.555  1000

Funny, compiler doesn't changes anything here!

Upvotes: 6

Dason
Dason

Reputation: 61953

You could define your own operator that does what you want.

> `%and%` <- function(x, y){as.logical(x*y)}
> NA %and% FALSE
[1] NA
> FALSE %and% NA
[1] NA
> NA %and% TRUE
[1] NA
> TRUE %and% NA
[1] NA

Upvotes: 9

Related Questions