Pythonにおけるnanの判定

Modified: | Tags: Python

Pythonの浮動小数点数float型には非数(not a number)を表すnanがある。nanの仕様はIEEE 754の浮動小数点規格によって定められている。

ここでは、Pythonにおけるnanの判定や比較について説明する。

本記事のサンプルコードではmathやpandas, NumPyを以下のようにインポートして使う。

import math

import numpy as np
import pandas as pd
source: nan_usage.py

なお、値が存在しないことを表すNonenanとは別物。Noneについての詳細は以下の記事を参照。

NumPyやpandasでnanを削除したり置換したりする方法について以下の記事を参照。

浮動小数点数float型の非数nan

Pythonでは浮動小数点数float型に非数を表すnanがある。float('nan')で生成できる。そのほかの生成方法については後述。

print(float('nan'))
# nan

print(type(float('nan')))
# <class 'float'>
source: nan_usage.py

例えば、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
source: nan_usage.py

nanの生成: float('nan'), math.nan, numpy.nan

上述のようにfloat('nan')nanを生成できる。大文字小文字を区別しないので'NaN''NAN'などでもよい。

print(float('nan'))
# nan

print(float('NaN'))
# nan

print(float('NAN'))
# nan
source: nan_usage.py

そのほか、math(標準ライブラリ)やNumPyでもnanを生成できる。NumPyではNaNNANもエイリアスとして定義されている。

print(math.nan)
# nan

print(np.nan)
# nan

print(np.NaN)
# nan

print(np.NAN)
# nan
source: nan_usage.py

いずれも同等で、次に紹介する判定方法で同じように判定される。

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
source: nan_usage.py

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]
source: nan_usage.py

なお、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
source: nan_usage.py

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
source: nan_usage.py

ある値がnanであるかを判定するには、==ではなく上述のmath.isnan(), numpy.isnan()を使う。

if文でのnanの判定

Pythonではbool型(True, False)以外のオブジェクトもif文の条件式などでは真偽のいずれかに判定される。例えば、空文字列''や数値0Falseでそれ以外の文字列や数値はTrue

bool()で確認できるように、nanTrueと判定される。

print(bool(float('nan')))
# True
source: nan_usage.py

上述のように==でも判定できないので、math.isnan(), numpy.isnan()を使う。

x = float('nan')

if math.isnan(x):
    print('This is nan.')
else:
    print('This is not nan.')
# This is nan.
source: nan_usage.py
x = 100

if math.isnan(x):
    print('This is nan.')
else:
    print('This is not nan.')
# This is not nan.
source: nan_usage.py

リスト中の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]
source: nan_usage.py

判定にmath.isnan(), numpy.isnan()を使うだけで、考え方は他の値を削除・置換する場合と同じ。詳細は以下の記事を参照。

NumPyやpandasにおいてnanを削除したり置換したりする方法について以下の記事を参照。

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
source: nan_usage.py

関連カテゴリー

関連記事