Pythonにおけるnanの判定
Pythonの浮動小数点数float型には非数(not a number)を表すnanがある。nanの仕様はIEEE 754の浮動小数点規格によって定められている。
ここでは、Pythonにおけるnanの判定や比較について説明する。
本記事のサンプルコードではmathやpandas, NumPyを以下のようにインポートして使う。
import math
import numpy as np
import pandas as pd
なお、値が存在しないことを表すNoneはnanとは別物。Noneについての詳細は以下の記事を参照。
- 関連記事: PythonにおけるNoneの判定
NumPyやpandasでnanを削除したり置換したりする方法について以下の記事を参照。
- 関連記事: NumPy配列ndarrayの欠損値np.nanを含む行や列を削除
- 関連記事: NumPy配列ndarrayの欠損値np.nanを他の値に置換
- 関連記事: pandasで欠損値NaNを削除(除外)するdropna
- 関連記事: pandasで欠損値NaNを置換(穴埋め)するfillna
浮動小数点数float型の非数nan
Pythonでは浮動小数点数float型に非数を表すnanがある。float('nan')で生成できる。そのほかの生成方法については後述。
print(float('nan'))
# nan
print(type(float('nan')))
# <class 'float'>
例えば、NumPyやpandasにおいて値が欠損したCSVファイルを読み込むとnanが発生する。pandasではNaNと表記される。
a = np.genfromtxt('data/src/sample_nan.csv', delimiter=',')
print(a)
# [[11. 12. nan 14.]
# [21. nan nan 24.]
# [31. 32. 33. 34.]]
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
nanの生成: float('nan'), math.nan, numpy.nan
上述のようにfloat('nan')でnanを生成できる。大文字小文字を区別しないので'NaN'や'NAN'などでもよい。
print(float('nan'))
# nan
print(float('NaN'))
# nan
print(float('NAN'))
# nan
そのほか、math(標準ライブラリ)やNumPyでもnanを生成できる。NumPyではNaNもNANもエイリアスとして定義されている。
print(math.nan)
# nan
print(np.nan)
# nan
print(np.NaN)
# nan
print(np.NAN)
# nan
いずれも同等で、次に紹介する判定方法で同じように判定される。
nanの判定: math.isnan(), np.isnan()
math.isnan()で値がnanかどうかを判定できる。
print(math.isnan(float('nan')))
# True
print(math.isnan(math.nan))
# True
print(math.isnan(np.nan))
# True
numpy.isnan()もある。
スカラー値のほか、リストやNumPy配列ndarrayといったarray-likeオブジェクトも引数に指定可能。要素ごとに判定され、ndarrayが返される。
print(np.isnan(float('nan')))
# True
print(np.isnan([float('nan'), math.nan, np.nan, 0]))
# [ True True True False]
なお、pandas.DataFrame, Seriesにはisna()およびそのエイリアスのisnull()メソッドがある。nanだけでなくNoneに対してもTrueと判定するという違いがある。詳細は以下の記事を参照。
math.isnan()やnp.isnan()にNoneを指定するとエラーになる。
比較演算子(<, >, ==, !=)に対する振る舞い
nanと比較する場合、相手がどんな値でも<, >, ==, <=, >=は常にFalse、!=は常にTrueとなる。
print(10 < float('nan'))
# False
print(10 > float('nan'))
# False
print(10 == float('nan'))
# False
print(10 != float('nan'))
# True
nan同士の比較でも同様。==, !=は直感に反した結果となるので要注意。
直観に反する帰結として、非数値は自分自身と等価ではないことになります。 例えば x = float('NaN') ならば、 3 < x, x < 3, x == x は全て偽で、x != x は真です。 6. 式 (expression) - 値の比較 — Python 3.11.3 ドキュメント
print(float('nan') == float('nan'))
# False
print(float('nan') != float('nan'))
# True
ある値がnanであるかを判定するには、==ではなく上述のmath.isnan(), numpy.isnan()を使う。
if文でのnanの判定
Pythonではbool型(True, False)以外のオブジェクトもif文の条件式などでは真偽のいずれかに判定される。例えば、空文字列''や数値0はFalseでそれ以外の文字列や数値はTrue。
bool()で確認できるように、nanはTrueと判定される。
print(bool(float('nan')))
# True
上述のように==でも判定できないので、math.isnan(), numpy.isnan()を使う。
x = float('nan')
if math.isnan(x):
print('This is nan.')
else:
print('This is not nan.')
# This is nan.
x = 100
if math.isnan(x):
print('This is nan.')
else:
print('This is not nan.')
# This is not nan.
リスト中のnanの削除・置換
リスト中のnanを削除・置換したい場合、リスト内包表記および三項演算子とmath.isnan(), numpy.isnan()による判定を組み合わせる。
l = [float('nan'), 0, 1, 2]
print(l)
# [nan, 0, 1, 2]
print([x for x in l if not math.isnan(x)])
# [0, 1, 2]
print([-100 if math.isnan(x) else x for x in l])
# [-100, 0, 1, 2]
判定にmath.isnan(), numpy.isnan()を使うだけで、考え方は他の値を削除・置換する場合と同じ。詳細は以下の記事を参照。
NumPyやpandasにおいてnanを削除したり置換したりする方法について以下の記事を参照。
- 関連記事: NumPy配列ndarrayの欠損値np.nanを含む行や列を削除
- 関連記事: NumPy配列ndarrayの欠損値np.nanを他の値に置換
- 関連記事: pandasで欠損値NaNを削除(除外)するdropna
- 関連記事: pandasで欠損値NaNを置換(穴埋め)するfillna
nanを含む演算
nanを含む+, -, *, /, **などの演算結果はすべてnanとなる。
print(float('nan') + 100)
# nan
print(float('nan') - 100)
# nan
print(float('nan') - 100)
# nan
print(float('nan') / 100)
# nan
print(float('nan') ** 100)
# nan