Menu

R の NA: 欠損値の扱い方(is.na, na.rm, na.omit)

NA は「不明」を意味し、触れたすべての計算に広がります。is.na() で検出し、na.rm = TRUE で読み飛ばし、意図的に削除または置換する方法を解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

NA は「不明」を意味し、そして広がる

実際のデータセットには穴があります。空欄のまま残されたアンケート項目、計測を取りこぼしたセンサー。R はそれぞれの穴を NAnot available)で表します。決定的な考え方はこうです。NA はゼロでも、空文字列でも、特別な数値でもありません。「ここに値はあるが、それが何かは分からない」 という意味です。

これを真剣に受け止めると、R の振る舞いは論理的に見えてきます。不明な数値に 1 を足すと? 不明です。4、8、そして何か不明なものの平均は? 不明です — 欠けている値は何であってもおかしくないので、平均も何にでもなりえます。

3 つとも NA を表示します。この伝染性はバグではなく機能です。R は、分かっていない答えを分かっているふりをして黙って返すことを拒みます。空白を静かに読み飛ばす表計算ソフトは、列の半分が欠損しているという事実を隠してしまいます。R は気づかせ、欠損値が自分の分析にとって何を意味すべきかを 決めさせます。この記事の残りは、その判断を意図的に行うための話です。

NA の判定: is.na() を使い、== は使わない

誰もが最初に落ちる罠がここにあります。欠損値を見つけたいので比較を書く — ところが動きません。

x == NANA NA NA を返します — TRUE は 1 つもありません。なぜでしょうか。「不明」の論理をたどってください。4 は何らかの不明な値と等しいでしょうか? 断言できません — その不明な値は 4 かもしれない のです。NA の枠自体も比較結果は NA になります。ある不明な値は別の不明な値と等しいでしょうか? 不明です。NA との比較が TRUE や FALSE を返すことは決してないので、判定としては役に立ちません。さらに悪いことに、[ ] の中で全要素が NA のマスクを使っても、期待どおりの選択にはなりません。

正しい道具は is.na() です。まさにこの問いに答えるために作られており、正直な論理値を返します: FALSE TRUE FALSE。そこから常用することになる 2 つの定型句があります。

sum(is.na(x)) は欠損値の個数を数えます(TRUE は 1 として合計されます)。新しいデータセットのすべての列に対して、何よりも先に実行しましょう。which(is.na(x)) は位置を特定します。そして x[!is.na(x)] は観測された値だけを残します — 論理マスクによる手動の除去で、いつもと同じ ベクトル の絞り込みパターンです。

要約で NA を読み飛ばす: na.rm = TRUE

普通は NA を手作業で取り除く必要はありません。R の要約関数には脱出口が組み込まれているからです — na.rm(NA remove)引数です。

na.rm = TRUE を指定すると、関数は欠損値を落として残りで計算します。4 と 8 の平均は 6 です。sum()sd()median()min()max()var()記述統計 の一族はすべてこれを受け取ります。

既定が FALSE であることに注目してください。これは R があなたのために意見を持っている部分です。欠損データを無視することを、黙って行われる既定ではなく、あなたが書き記す明示的な選択にしたいのです。na.rm = TRUE と打つとき、あなたは「ここでは欠損値を無視して安全だ」と主張しています — センサーがランダムにいくつか計測を落としたのなら真ですが、たとえば最も所得の低い層が所得の質問を飛ばしたのなら、危険なほど偽です。この引数はその判断の責任をあなたに負わせます。

不完全な行の削除: na.omit() と complete.cases()

データフレーム では欠損はセルに宿りますが、分析はしばしば行単位で進みます。そのため一般的な操作は、NA を含む行を落とすことです。

na.omit(df) は NA を 1 つ以上含むすべての行を除いたデータフレームを返します — ここでは Rosa だけが残ります。complete.cases(df) は同じ考え方の背後にある行の論理マスク(TRUE FALSE FALSE)を返し、それで添字付けすると同じ結果が得られますが、2 つの利点があります。失うものを事前に数えられること(sum(!complete.cases(df)))、そしてどの列を対象にするかを限定できることです — df[complete.cases(df[, "age"]), ]age が欠損している行だけを落とすので、Mia はスコアが不明でも残ります。

この列を限定する形は見た目以上に重要です。幅の広いデータフレームに na.omit() をかけると、分析するつもりもなかった列の NA のせいで行の大半が静かに捨てられることがあります。落とす前に、何行を、なぜ落とすのかを把握しておきましょう。

NA の置換

行を削除するのではなく、穴を埋めるのが正しい手であることもあります。定型句は is.na() と代入の組み合わせです。

visits が欠損している枠に 0 を入れる」と読みます。これが正当なのは、NA が既知の値を 符号化している 場合に限られます — 来店記録のない顧客は本当に来店回数がゼロだった、という場合です。

しかし、それが本当に成り立つかどうかには誠実であってください。NA が「計測に失敗した」を意味するなら、0 を代入することはデータの捏造です。欠損したテスト得点を 0 に置き換えると、その学生たちが 0 点だったかのように平均が引き下げられますが、実際には何点だったか分からないのです。上の平均(2.4)と、元データの na.rm 平均(4)を比べてみてください。同じデータでも、主張はまったく違います。平均や中央値で置き換える方が歪みは小さいものの、それでも変動を過小評価します。原則はこうです。欠損した項目が実際にはその値だった理由を平易な言葉で言えるときにだけ補完しましょう。そうでなければ NA を保ったまま na.rm を使ってください — 「不明」がデータセットの中で最も真実に近い値であることは少なくありません。

NA と NULL と NaN と Inf

R には見た目が似ていて意味がまったく異なる特別な値が 4 つあります。

意味長さ典型的な発生源
NA値は存在するが不明1(枠を占める)欠損データ
NULLオブジェクトが全く無い0(枠を占めない)削除したリスト要素、空の結果
NaN答えが定義されない演算10 / 0, log(-1)
Inf表現できる範囲を超えた数11 / 0, オーバーフロー

実務で効いてくる違いはこうです。NULL はベクトルの中で消えます(c(1, NULL, 3) は要素が 2 つ — 欠損した観測を表せません)が、NA は自分の場所を保ちます。NaN は欠損として数えられ(is.na(NaN)TRUE なので na.rm はこれも取り除きます)、逆は成り立ちません — is.nan(NA)FALSE です。そして Inf は欠損では ありません。実在する比較可能な数値なので(Inf > 1e300TRUE)、na.rm では取り除けません。通常の数値だけに絞るには is.finite() を使います。

補足として、NA には型付きの種類(NA_integer_, NA_real_, NA_character_)が密かに存在し、1 つの型という規則を壊さずにどんなベクトルにも収まれるようになっています。自分で打つことはほとんどありませんが、パッケージのコードや dplyr のエラーメッセージで見かけるでしょう。

この記事のまとめ

  • NA は「不明」を意味し、不明は伝染します。NA に触れる計算はすべて NA を返します — これは設計です。
  • 判定は is.na() で行い、== NA は使いません。穴の数は sum(is.na(x)) で数えます。
  • na.rm = TRUE は要約関数に NA を読み飛ばさせます — 欠損値を無視してよいという、呼び出しごとの明示的な判断です。
  • na.omit() は不完全な行をまとめて落とします。complete.cases() はマスクを返し、数えることも、重要な列に限定することもできます。
  • NA の置換(x[is.na(x)] <- value)は、欠損値が実際には何だったのかを正当化できるときにだけ行いましょう。
  • NA ≠ NULL ≠ NaN ≠ Inf: 欠損した値、不在のオブジェクト、定義されない演算、限界を超えた数。

次は 関数 — ロジックを再利用可能で名前の付いた部品にまとめる方法です。

よくある質問

R で mean() が NA を返すのはなぜですか?

ベクトル内の少なくとも 1 つの値が NA であり、NA は伝染するからです。入力のどれかが不明なら、R は答えも不明だと言います。na.rm = TRUE を渡すと(mean(x, na.rm = TRUE))、存在する値だけで平均を計算します。ほとんどの要約関数(sum, sd, median, min, max)が同じ引数を受け取ります。

R で NA を判定するにはどうすればよいですか?

is.na(x) を使います。値が欠損している位置で TRUE を返します。x == NA で判定してはいけません。不明な値と何かを比較すると TRUE でも FALSE でもなく NA になるため、判定は静かに失敗します。sum(is.na(x)) は欠損値の個数を数え、which(is.na(x)) はその位置を見つけます。

R でデータフレームから NA を含む行を削除するにはどうすればよいですか?

na.omit(df) は NA を 1 つでも含む行をすべて削除します。より細かく制御したい場合、complete.cases(df) は完全に観測された行を示す論理ベクトルを返すので、df[complete.cases(df), ] で同じことを明示的に行えます。さらに、重要な列だけに適用することもできます。例: df[complete.cases(df[, c("age", "score")]), ]

R における NA と NULL の違いは何ですか?

NA は欠損した です。ベクトル内で 1 つの枠を占め、長さは 1 です。NULL は オブジェクトの不在 で、長さは 0 であり、ベクトルに入れると消えます: c(1, NULL, 3) は要素が 2 つしかありません。欠損したデータ点には NA を使います。NULL はリスト要素を削除するときや、空の戻り値として現れます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める