Rで欠損値の有無を判定する函数 anyNA()

概要
Rの函数 anyNA() は引数のオブジェクトに欠損値が含まれているかどうかを高速に返す函数であり、欠損値が含まれていれば TRUE を返す。

anyNA() で欠損値の有無を調べる

データを集める際に、何らかの理由で一部のデータが得られないことがある。例えば、アンケートを行う際に、ある回答者が年齢について回答してくれなかったために、その回答者の年齢のデータが得られないということがある。あるいは温度計の故障で、ある地点の温度のデータが得られないということもある。こうして得られなかった値のことを欠損値と呼ぶ。データの処理を始める際には、その中に欠損値があるかどうかを確認することが重要である。もし欠損値を含むようであったら、それなりの対応を取らなくてはならない。

統計解析用の言語である R には、anyNA()という有用な函数があり、これで欠損値の有無を調べることができる。この函数は、ベクトル・行列などのオブジェクトを引数に取り、そのオブジェクトに欠損値が含まれているかどうかを返す函数である。anyNA() は欠損値が含まれていれば、TRUEを返す。逆に欠損値が含まれていなければ、FALSEを返す。なお、R では、欠損値は NA と表示される。

anyNA() 函数の使用例を見てみよう。以下で、x1 にはNA(欠損値)が含まれていない。よって、anyNA(x1) はFALSEを返す。

x1 <- c(7, 6)
anyNA(x1)

これに対して、以下の例での x2 にはNA(欠損値)が含まれている。よって、anyNA(x2) はTRUEを返す。

x2 <- c(5, NA)
anyNA(x2)

anyNA() はベクトルだけでなく、行列・データフレームも引数に取ることが可能である。行列を引数に取った場合、行列全体のうちどこか1箇所でもNA(欠損値)が含まれていれば、TRUE が返される。データフレームの場合も同様である。

なお、この anyNA() 函数は2014年4月10日にリリースされたR のバージョン3.1.0で初めて導入された函数である。古い R には入っていないので注意が必要だ。古い R で anyNA() と同様の処理をしたければ、any() と is.na() を組み合わせて処理を行えばよい。すなわち、anyNA(…) とする代わりに、any(is.na(…)) とすればよい。anyNA(…) と any(is.na(…)) とは同じ結果を返す。ただし、anyNA(…) の方が計算が速くなるので、バージョン3.1.0以降を使える環境であれば、anyNA(…) を使う方が望ましい。

anyNA(…) と any(is.na(…)) の速度の比較

先に述べたように、any() と is.na() を組み合わせて計算するより、anyNA()で計算した方が欠損値があるかどうかの判定を高速に行うことができる。このことを実際に比較してみよう。

まず、判定対象となるオブジェクト x を以下のように用意する。以下のコードの1行目では、0が1億個並んだベクトルを x に代入している。そして、2行目では、1億個の0のあとに、NA(欠損値)を付け加えている。最終的に、x は1億と1個の要素が含まれていることになる。

x <- numeric(100000000)
x <- c(x, NA)

この x に対して欠損値があるかどうかを調べる際に、anyNA(…) と any(is.na(…)) とで、どれだけかかる時間が違うかを測定してみよう。Rでの処理にかかる時間を測定したいときは、system.time() 函数を使うのが便利である。以下では、1行目で anyNA(…) を使った場合の時間を測定し、2行目で any(is.na(…)) を使った場合の時間を測定している。

system.time(anyNA(x))
system.time(any(is.na(x)))

私の環境 [1] で実施したところ、anyNA(…) の場合は0.11秒を要し、any(is.na(…)) の場合は0.34秒を要した。anyNA(…) の方がかなり速く計算できるのだ。

ただし、状況によっては両者が同じ速度になったり、anyNA(…) の方が遅くなったりする。実は仕様上、高速に計算する手法が用意されていないときは anyNA(…) という函数を実行しても内部的には any(is.na(…)) を用いられるということになっているためだ。とは言え、単純なベクトルや行列に対して適用するならば、大概 anyNA(…) の方が高速である。しかし、リストに対して適用する場合、 anyNA(…) の方がかえって時間がかかることもある。

脚注
  1. なお、計算にかかる時間は環境によって異なるので、ここでの計算時間はあくまでも一例として捉えてほしい。とは言え、ほとんどの状況下で anyNA(…) の方が高速なはずだ。 []