Introduction

R’s complex type (class) also supports missing values, i.e., NAs, and NaN (“Not a Number”) values as the numeric (double) is known for.

However, complex vectors (or matrices) are used much more rarely, and some details of R’s implementation have been unchanged since written in the early days of R (in the 90’s), even though they have not been quite consistent with the handling of NA and NaN for numeric objects.

First, note, e.g., from ? as.complex that R has a builtin for the “complex NA”,

NA_complex_
## [1] NA

whose purpose is to model complex missing values and which is equivalent to both real and imaginary part being NA:

identical(NA_complex_, complex(re = NA, im = NA))
## [1] TRUE

But what about complex NaNs?
They should be different from the above “complex NA”, but in R <= 3.3.2, you cannot use as.complex(NaN) to construct them, but e.g., complex(re = ., im = .)

cNaN  <- complex(re = NaN, im = NaN)
c1NaN <- complex(re = 1,   im = NaN);  c(cNaN = cNaN, c1NaN = c1NaN)
##     cNaN    c1NaN 
## NaN+NaNi   1+NaNi

Let’s use a small example vector to illustrate the behavior,

r <- c(NA,-1:2,NaN)
cc <- c(-NaN*(1+1i), NA + 1i*NaN,
        complex(re=r, im=NaN),
        complex(re=r, im= NA))
cc
##  [1]       NA       NA       NA  -1+NaNi   0+NaNi   1+NaNi   2+NaNi
##  [8] NaN+NaNi       NA       NA       NA       NA       NA       NA

Now already the printing is a bit surprising (if you know a bit more), or if you compare it with the following representation that reveals more about the internal structure:

(f.cc <- sapply(cc, deparse))
##  [1] "NA_complex_"                      "NA_complex_"                     
##  [3] "complex(real=NA, imaginary=NaN)"  "complex(real=-1, imaginary=NaN)" 
##  [5] "complex(real=0, imaginary=NaN)"   "complex(real=1, imaginary=NaN)"  
##  [7] "complex(real=2, imaginary=NaN)"   "complex(real=NaN, imaginary=NaN)"
##  [9] "NA_complex_"                      "complex(real=-1, imaginary=NA)"  
## [11] "complex(real=0, imaginary=NA)"    "complex(real=1, imaginary=NA)"   
## [13] "complex(real=2, imaginary=NA)"    "complex(real=NaN, imaginary=NA)"
names(cc) <- abbreviate(f.cc, minlength=11)
## all these are neither finite nor infinite :
stopifnot(!is.infinite(cc),
          !is.finite(cc))

Now to the R interface, i.e., important ‘indicator’ functions such as is.na() which should be TRUE for all NA and NaN, or is.nan() which should be TRUE only for NaNs.

is.NA.c <- sapply(cc, function(.) identical(., NA_complex_))

ctab <- cbind(cc = format(cc), is.NA.c,
              is.nan = is.nan(cc), is.na = is.na(cc),
          duplic. = duplicated(cc), match. = match(cc,cc))
print(ctab, quote=FALSE, right=TRUE)
##                   cc is.NA.c is.nan is.na duplic. match.
## NA_complex_       NA    TRUE  FALSE  TRUE   FALSE      1
## NA_complex_       NA    TRUE  FALSE  TRUE    TRUE      1
## c(=NA,i=NN)       NA   FALSE   TRUE  TRUE   FALSE      3
## c(=-1,i=NN)  -1+NaNi   FALSE   TRUE  TRUE   FALSE      4
## cm(=0,i=NN)   0+NaNi   FALSE   TRUE  TRUE   FALSE      5
## cm(=1,i=NN)   1+NaNi   FALSE   TRUE  TRUE    TRUE      3
## cm(=2,i=NN)   2+NaNi   FALSE   TRUE  TRUE   FALSE      7
## c(=NN,i=NN) NaN+NaNi   FALSE   TRUE  TRUE    TRUE      7
## NA_complex_       NA    TRUE  FALSE  TRUE    TRUE      1
## c(=-1,i=NA)       NA   FALSE  FALSE  TRUE    TRUE      1
## cm(=0,i=NA)       NA   FALSE  FALSE  TRUE   FALSE     11
## cm(=1,i=NA)       NA   FALSE  FALSE  TRUE   FALSE     12
## cm(=2,i=NA)       NA   FALSE  FALSE  TRUE   FALSE     13
## c(=NN,i=NA)       NA   FALSE   TRUE  TRUE    TRUE      3

Note that for R-devel, after 2015-09-22 (rev 69410), the above gives

##                   cc is.NA.c is.nan is.na duplic. match.
## c(=NN,i=NN) NaN+NaNi   FALSE   TRUE  TRUE   FALSE      1
## c(=NN,i=NN) NaN+NaNi   FALSE   TRUE  TRUE    TRUE      1
## c(=NA,i=NN)       NA   FALSE   TRUE  TRUE   FALSE      3
## c(=-1,i=NN)  -1+NaNi   FALSE   TRUE  TRUE   FALSE      4
## cm(=0,i=NN)   0+NaNi   FALSE   TRUE  TRUE   FALSE      5
## cm(=1,i=NN)   1+NaNi   FALSE   TRUE  TRUE    TRUE      3
## cm(=2,i=NN)   2+NaNi   FALSE   TRUE  TRUE   FALSE      7
## c(=NN,i=NN) NaN+NaNi   FALSE   TRUE  TRUE    TRUE      1
## NA_complex_       NA    TRUE  FALSE  TRUE   FALSE      9
## c(=-1,i=NA)       NA   FALSE  FALSE  TRUE    TRUE      9
## cm(=0,i=NA)       NA   FALSE  FALSE  TRUE   FALSE     11
## cm(=1,i=NA)       NA   FALSE  FALSE  TRUE   FALSE     12
## cm(=2,i=NA)       NA   FALSE  FALSE  TRUE   FALSE     13
## c(=NN,i=NA)       NA   FALSE   TRUE  TRUE    TRUE      3

The last two columns are about match() and duplicated() — which are well consistent with unique(), but behave quite peculiarly (even after R-devel revision 69410):

stopifnot(identical(match(cc,cc) != seq_along(cc),
                    duplicated(cc)),
          all.equal(unique(cc),
                    cc[!duplicated(cc)], # <- has names, hence :
                    check.attributes = FALSE))

and really they seem to behave wrongly, see the "duplic." column above, or also

which(duplicated(cc))
## [1]  2  6  8  9 10 14

and not much different in R-devel, after 2015-09-22 (rev 69410) :

## [1]  2  6  8    10 14

All the above was produced in the following R environment

sessionInfo()
## R version 3.2.2 (2015-08-14)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Fedora 22 (Twenty Two)
## 
## locale:
##  [1] LC_CTYPE=de_CH.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=de_CH.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=de_CH.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=de_CH.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] graphics  grDevices datasets  stats     utils     methods   base     
## 
## other attached packages:
## [1] sfsmisc_1.0-29
## 
## loaded via a namespace (and not attached):
##  [1] magrittr_1.5    formatR_1.2.1   htmltools_0.2.6 tools_3.2.2    
##  [5] yaml_2.1.13     stringi_0.5-5   rmarkdown_0.8   knitr_1.11     
##  [9] stringr_1.0.0   digest_0.6.8    evaluate_0.8