R
データ分析
欠損値

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

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()で行数を確認するようにしましょう。本題から逸れますが、もし想定以上に減っていた場合は平均値などで置換することも想定したほうがよいかもしれません。