NA、NaN、NULL、Infとは何か
ざっくりまとめ
データ型の観点から理解する
Rのデータ型概要
データ型 | データ型の確認関数 | 表記例 | 欠損値 |
---|---|---|---|
実数 | is.numeric | 3、-1.5、 .1、1e+2、1.4e-2、Inf、-Inf、NaN | 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()で行数を確認するようにしましょう。本題から逸れますが、もし想定以上に減っていた場合は平均値などで置換することも想定したほうがよいかもしれません。