Edited at

【R言語】いい加減NAとNaNとNULL(ついでにInf)の違いをはっきりさせておく

More than 1 year has passed since last update.


NA、NaN、NULL、Infとは何か


ざっくりまとめ


由来
日本語名
具体例
説明
データ型確認関数
イメージ画像

NA
Not Available
欠損値
NA
本来データが存在しているが、何らかの理由でデータが存在していない状態を示す
is.na()

NaN
Not a Number
非数値
0/0
計算不可能な式の結果として表される
is.nan()

NULL
null
非存在
NULL
もともと値が存在していないことを示す
is.null()
nc69175.jpg

Inf
Infinity
無限大
1/0
無限に発散する計算結果として表される
is.infinite()
110622infinity_02.jpg


データ型の観点から理解する


Rのデータ型概要

データ型
データ型の確認関数
表記例
欠損値

実数
is.numeric
3、-1.5、 .1、1e+2、1.4e-2、Inf、-InfNaN

NA_real_

文字列
is.character
'abc'、"森進一"、"髙田延彦\n星新一"
NA_character_

整数
is.integer
1L (単に1だけだと実数型になる)
NA_integer_

複素数
is.complex
2+3i
NA_complex_

論理
is.logical
TRUE、FALSE、T、F
NA

NULL
is.null
NULL
なし


Inf、NaNについて

Inf、NaNは実数型の一種です。もともと、Infは無限に発散する式の計算結果を示すもの、NaNは計算不可能な式の結果を示すものであり、どちらも数値計算の結果です。よって、実数型にあてはまります。

# 値を準備

(nan_num <- 0/0) # NaN
(inf_num <- 1/0) # Inf
# NaN、Infそれぞれ確認
is.nan(nan_num)# [1] TRUE
is.infinite(inf_num)# [1] TRUE
# 実数型かどうか確認
is.numeric(nan_num) # [1] TRUE
is.numeric(inf_num) # [1] TRUE

注意点として、NaNはis.naで調べた結果がTRUEになるので覚えておきましょう。

is.na(nan_num) # [1] TRUE


NAについて

一番右端の列の、NA_real_NA_characterなどは見慣れない表記なのではないでしょうか。実はRの内部では、各データ型に対して欠損値NAを用意しています。例えば、文字列のベクトルの中にある欠損値NAは、文字列型のNAとし、実数型のベクトルの中にある欠損値NAは、実数型のNAとして扱っています。実際にRで確認したものが以下になります。

# 3番目の要素にNAを入れたベクトルを用意

chr <- c("a","b",NA)
num <- c(1L,9L,NA)
# データ型を確認
typeof(chr[3]) # [1] "character"
typeof(num[3]) # [1] "numeric"
# NAかどうか確認
is.na(chr[3]) # [1] TRUE
is.na(num[3]) # [1] TRUE
# NA_character_かどうか確認
identical(chr[3],NA_character_) # [1] TRUE
identical(num[3],NA_character_) # [1] FALSE
# NA_integer_かどうか確認
identical(chr[3],NA_integer_) # [1] FALSE
identical(num[3],NA_integer_) # [1] TRUE


NULLについて

NULLはデータが存在しないことを示すデータ型です。NAは本来データが存在しているが何らかの理由で存在しないことを示すが、NULLはもともと存在しないことを示します。

存在しないのに、NULLという表記がある事自体が少しややこしいです。

class(NULL) # [1] "NULL"

is.null(NULL) # [1] TRUE


データ型についてまとめ


  • NaN、Inf


    • 実数型



  • NA 


    • 各データ型それぞれに存在



  • NULL


    • データが存在しないことを示すデータ型



ということがわかりました。


NA、NaN、NULL、Infが原因でハマりやすいポイント


NAやNaN対してどんな計算をしてもNA、NaNになる

# NAを含んだ計算

NA + 8 # [1] NA
2 * NA # [1] NA
sum(c(1, 2, 4, NA)) # [1] NA
# NaNを含んだ計算
NaN + 8 # [1] NaN
NaN * 2 # [1] NaN
sum(c(1, 2, 4, NaN)) # [1] NaN
# ちなみに、NAとNaN両方含まれる計算をすると、先に計算される方が優先される
NaN * 10 # [1] NaN
NA * NaN * 10 # [1] NA

データに対して何らかの集計・統計処理などをしているとき、結果がNAで出てきてしまうことはよくある。そんなときはパソコンをバンバンするのではなく、まずはデータにNAが含まれていることを疑ったほうがよい。NAの探し方はこの記事を読み進めると出て来る。


NULLはベクトルの要素になれない

# NULLを含んだベクトル?

null_vec <- c(1,2, NULL, 4)
null_vec # [1] 1 2 4
length(null_vec) # [1] 3

だってそもそも、存在しない存在なので。


NA、NaN、Infの対処法

NA、NaN、Infが存在するデータについて、以下の3点で対処法を整理します


  • NA、NaN、Infが含まれるかどうか確認する

  • NA、NaN、Infを別の値で置換する

  • NAやNaNがある行を取り除く

# 例としてNAなどが含まれたirisデータを準備

data(iris)
iris[,1:4][iris[,1:4] %% 1.1 == 0] <- NA
iris[,1:4][iris[,1:4] %% 0.9 == 0] <- NaN
iris[,1:4][iris[,1:4] %% 0.7 == 0] <- Inf
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 5.1 Inf Inf 0.2 setosa
# 2 4.9 3.0 Inf 0.2 setosa
# 3 4.7 3.2 1.3 0.2 setosa
# 4 4.6 3.1 1.5 0.2 setosa
# 5 5.0 NaN Inf 0.2 setosa
# 6 NaN 3.9 1.7 0.4 setosa


NA、NaN、Infが含まれるかどうか確認する


まずはsummaryがおすすめ。

最も手軽に各列の欠損値の状況を調べることができます

summary(iris)

# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# Min. :4.3 Min. :2.0 Min. :1.000 Min. :0.1 setosa :50
# 1st Qu.:5.1 1st Qu.:3.0 1st Qu.:1.700 1st Qu.:0.2 versicolor:50
# Median :6.0 Median :3.1 Median :4.700 Median :1.3 virginica :50
# Mean :Inf Mean :Inf Mean : Inf Mean :Inf
# 3rd Qu.:6.7 3rd Qu.:3.7 3rd Qu.:5.875 3rd Qu.:2.0
# Max. :Inf Max. :Inf Max. : Inf Max. :Inf
# NA's :29 NA's :17 NA's :20 NA's :18

上記の出力結果を読み解き方を、一番左のSepal.Length列を例に説明すると、



  • NA's :29


    • 欠損値(NAもしくはNaN)が29個含まれていることがわかる




  • Mean :Inf


    • 平均がInfなので、データにInfが含まれていることがわかる



と読み解くことができます。


各列の平均値などは無しで、純粋にNA、NaN、Infの数だけを集計したい

sapply()と、is.na()、sum()を組み合わせることで可能です。

sapply(iris, function(y) sum(is.na(y)))

# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 29 17 20 18 0

sapply(iris, function(y) sum(is.nan(y)))
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 19 13 12 12 0

sapply(iris, function(y) sum(is.infinite(y)))
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 7 20 21 8 0


NA、NaN、Infを別の値で置換する


データフレーム全体に対して置換をしたい

# データフレーム全体のNAを0で置換

iris[is.na(iris)] <- 0
# データフレーム全体のNaNを0で置換
iris[is.nan(iris)] <- 0
# データフレーム全体のInfを0で置換
iris[is.infinite(iris)] <- 0


データフレームの列に対して置換をしたい

#Sepal.Length列のNAを0で置換

iris$Sepal.Length <- ifelse(is.na(iris$Sepal.Length),0,iris$Sepal.Length)
#Sepal.Length列のNaNを0で置換
iris$Sepal.Length <- ifelse(is.nan(iris$Sepal.Length),0,iris$Sepal.Length)
#Sepal.Length列のInfを0で置換
iris$Sepal.Length <- ifelse(is.infinite(iris$Sepal.Length),0,iris$Sepal.Length)


NAやNaNがある行を取り除く

na.omit()を使うことで、NA(NaN)を含む全ての行を削除することができます

iris2 <- na.omit(iris)

# NAが削除されたかを確認
sapply(iris2, function(y) sum(is.na(y)))
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 0 0 0 0 0 #消えてる!
nrow(iris2)
# [1] 79

na.omit()で処理した後は、必ずnrow()で行数を確認するようにしましょう。本題から逸れますが、もし想定以上に減っていた場合は平均値などで置換することも想定したほうがよいかもしれません。