pandasにおける欠損値(nan, None, pd.NA)
pandasにおいて欠損値(Missing value, NA: not available)は主にnan
(not a number、非数)を用いて表される。そのほか、None
も欠損値として扱われる。
本記事のサンプルコードのpandasのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。説明のため、mathおよびNumPyも使う。
import math
import numpy as np
import pandas as pd
print(pd.__version__)
# 2.0.3
ファイルの読み込みなどで生じる欠損値
値が欠損したCSVファイルなどを読み込むとnan
が生じる。pandas.DataFrame
, Series
のprint()
出力ではNaN
と表記される。
df = pd.read_csv('data/src/sample_pandas_normal_nan.csv')[:3]
print(df)
# name age state point other
# 0 Alice 24.0 NY NaN NaN
# 1 NaN NaN NaN NaN NaN
# 2 Charlie NaN CA NaN NaN
isnull()
, dropna()
, fillna()
といったメソッドで判定・削除・置換などができる。
- 関連記事: pandasで欠損値NaNが含まれているか判定、個数をカウント
- 関連記事: pandasで欠損値NaNを削除(除外)するdropna
- 関連記事: pandasで欠損値NaNを置換(穴埋め)するfillna
print(df.isnull())
# name age state point other
# 0 False False False True True
# 1 True True True True True
# 2 False True False True True
print(df.dropna(how='all'))
# name age state point other
# 0 Alice 24.0 NY NaN NaN
# 2 Charlie NaN CA NaN NaN
print(df.fillna(0))
# name age state point other
# 0 Alice 24.0 NY 0.0 0.0
# 1 0 0.0 0 0.0 0.0
# 2 Charlie 0.0 CA 0.0 0.0
データ型dtype
がobject
の列のnan
はPython組み込みのfloat
型で、floatXX
の列のnan
はNumPyのnumpy.floatXX
型。どちらも同じように欠損値として処理されるので特に気にする必要はない。
print(df.dtypes)
# name object
# age float64
# state object
# point float64
# other float64
# dtype: object
print(df.at[1, 'name'])
# nan
print(type(df.at[1, 'name']))
# <class 'float'>
print(df.at[1, 'age'])
# nan
print(type(df.at[1, 'age']))
# <class 'numpy.float64'>
ファイル読み込みのほか、reindex()
やmerge()
などで値が存在しない場合も欠損値としてnan
が用いられる。
nan(not a number)は欠損値
Pythonではfloat('nan')
やmath.nan
, np.nan
でnan
を生成できる。pandasにおいてnan
は欠損値として扱われる。
- 関連記事: Pythonにおけるnanの判定
s_nan = pd.Series([float('nan'), math.nan, np.nan])
print(s_nan)
# 0 NaN
# 1 NaN
# 2 NaN
# dtype: float64
print(s_nan.isnull())
# 0 True
# 1 True
# 2 True
# dtype: bool
Noneも欠損値
pandasではNone
も欠損値として扱われる。None
はPythonの組み込み定数。
- 関連記事: PythonにおけるNoneの判定
print(None)
# None
print(type(None))
# <class 'NoneType'>
数値列では、None
を含むpandas.DataFrame
やSeries
を生成またはNone
を要素に代入した時点で、None
がnan
に変換される。
s_none_float = pd.Series([None, 0.1, 0.2])
s_none_float[2] = None
print(s_none_float)
# 0 NaN
# 1 0.1
# 2 NaN
# dtype: float64
print(s_none_float.isnull())
# 0 True
# 1 False
# 2 True
# dtype: bool
nan
は浮動小数点数float
なので、他の値が整数int
であっても、None
がnan
に変換されるとその列のデータ型dtype
はfloat
になる。
s_none_int = pd.Series([None, 1, 2])
print(s_none_int)
# 0 NaN
# 1 1.0
# 2 2.0
# dtype: float64
object
列のNone
はNone
のままだが、isnull()
などでは欠損値として判定される。当然、dropna()
, fillna()
などでも処理の対象になる。
s_none_object = pd.Series([None, 'abc', 'xyz'])
print(s_none_object)
# 0 None
# 1 abc
# 2 xyz
# dtype: object
print(s_none_object.isnull())
# 0 True
# 1 False
# 2 False
# dtype: bool
print(s_none_object.fillna(0))
# 0 0
# 1 abc
# 2 xyz
# dtype: object
文字列'NaN'や'None'などは欠損値ではない
文字列'NaN'
や'None'
などは表示上は見分けがつかないが、あくまでも文字列なので欠損値とはみなされない。空文字列''
も欠損値とはみなされない。
s_str = pd.Series(['NaN', 'None', ''])
print(s_str)
# 0 NaN
# 1 None
# 2
# dtype: object
print(s_str.isnull())
# 0 False
# 1 False
# 2 False
# dtype: bool
欠損値として扱いたい値はreplace()
メソッドでfloat('nan')
などに置き換えればよい。
s_replace = s_str.replace(['NaN', 'None', ''], float('nan'))
print(s_replace)
# 0 NaN
# 1 NaN
# 2 NaN
# dtype: float64
print(s_replace.isnull())
# 0 True
# 1 True
# 2 True
# dtype: bool
なお、CSVファイルの空白の値がnan
として読み込まれるように、read_csv()
などのファイルを読み込む関数では空文字列''
や文字列'NaN'
, 'null'
などがデフォルトで欠損値とみなされnan
に置き換えられる。詳細は以下の記事を参照。
無限大infはデフォルトでは欠損値ではない(設定で変更可能)
Pythonの浮動小数点数float
には無限大を表すinf
がある。
無限大inf
はデフォルトでは欠損値とみなされない。
s_inf = pd.Series([float('inf'), -float('inf')])
print(s_inf)
# 0 inf
# 1 -inf
# dtype: float64
print(s_inf.isnull())
# 0 False
# 1 False
# dtype: bool
pandasのオプション設定でpd.options.mode.use_inf_as_na
をTrue
にすると、pandas.DataFrame
, Series
の中のinf
はnan
に変換されて欠損値として扱われる。None
とは異なりobject
列でもnan
に変換される。
pd.options.mode.use_inf_as_na = True
print(s_inf)
# 0 NaN
# 1 NaN
# dtype: float64
print(s_inf.isnull())
# 0 True
# 1 True
# dtype: bool
s_inf_object = pd.Series([float('inf'), -float('inf'), 'abc'])
print(s_inf_object)
# 0 NaN
# 1 NaN
# 2 abc
# dtype: object
print(s_inf_object.isnull())
# 0 True
# 1 True
# 2 False
# dtype: bool
pandasのオプション設定については以下の記事を参照。
- 関連記事: pandasのオプション設定を確認・変更する方法
pd.NAは実験的な欠損値(2.0.3時点)
pandas1.0.0
で実験的(Experimental)な欠損値としてpd.NA
が導入された。
print(pd.NA)
# <NA>
print(type(pd.NA))
# <class 'pandas._libs.missing.NAType'>
nan
同士を==
で比較するとFalse
となるが、pd.NA
同士を==
で比較するとpd.NA
となる(R言語と同じ仕様)。
print(float('nan') == float('nan'))
# False
print(pd.NA == pd.NA)
# <NA>
もちろんisnull()
やfillna()
などの対象となる。
s_na = pd.Series([None, 1, 2], dtype='Int64')
print(s_na)
# 0 <NA>
# 1 1
# 2 2
# dtype: Int64
print(s_na.isnull())
# 0 True
# 1 False
# 2 False
# dtype: bool
print(s_na.fillna(0))
# 0 0
# 1 1
# 2 2
# dtype: Int64
上記サンプルコードに登場するInt64
については以下を参照。欠損値を含んでいても他の整数の値が浮動小数点数に変換されることなく処理できる。
なお、2.0.3
(2023年6月時点)でも「Experimental」。仕様が変わる可能性もあるので本格的に使うのは難しそうだが、とりあえず存在は知っておくといいかもしれない。
Warning
Experimental: the behaviour of pd.NA can still change without warning. Working with missing data - Experimental NA scalar to denote missing values — pandas 2.0.3 documentation