NumPy配列ndarrayの欠損値np.nanを他の値に置換

Modified: | Tags: Python, NumPy

NumPy配列ndarrayの欠損値NaNnp.nan)を置換するには、np.nan_to_num()np.isnan()を利用する。任意の値に置き換えたり、欠損値NaNを除外した要素の平均値に置き換えたりできる。

欠損値を置換するのではなく削除する方法については以下の記事を参照。

pandasでの欠損値の処理については以下の記事を参照。

本記事のサンプルコードのNumPyのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。

import numpy as np

print(np.__version__)
# 1.26.1

欠損値NaNの発生・生成・判定

np.genfromtxt()でCSVファイルを読み取ると、デフォルトではデータが欠落した箇所が欠損値NaN(Not a Number: 非数)となる。print()での出力時はnanと表示される。

a = np.genfromtxt('data/src/sample_nan.csv', delimiter=',')
print(a)
# [[11. 12. nan 14.]
#  [21. nan nan 24.]
#  [31. 32. 33. 34.]]

明示的に欠損値NaNを生成したいときはnp.nanfloat('nan')などを使う。

a_nan = np.array([0, 1, np.nan, float('nan')])
print(a_nan)
# [ 0.  1. nan nan]

欠損値同士を==で比較してもFalseとなってしまうため、欠損値かどうかを判定するにはnp.isnan()を使う。

print(np.nan == np.nan)
# False

print(np.isnan(np.nan))
# True

np.isnan()ではndarrayの各要素に対して欠損値NaNかどうかを判定できる。

print(a_nan == np.nan)
# [False False False False]

print(np.isnan(a_nan))
# [False False  True  True]

np.genfromtxt()の引数filling_valuesを指定

元となるCSVファイルのデータが欠落している場合、np.genfromtxt()で読み込むときに引数filling_valuesを指定すると、欠落箇所を任意の値で埋めることができる。

例えば、0で埋めたい場合は以下の通り。

a_fill = np.genfromtxt('data/src/sample_nan.csv', delimiter=',',
                       filling_values=0)
print(a_fill)
# [[11. 12.  0. 14.]
#  [21.  0.  0. 24.]
#  [31. 32. 33. 34.]]

欠損値ではない要素の平均値などで埋めたい場合はnp.genfromtxt()での読み込み時には指定できない。以降に説明する方法を使う。

np.nan_to_num()で欠損値NaNを置換

配列ndarrayの欠損値NaNを置換するにはnp.nan_to_num()を使う。

なお、np.nan_to_num()は欠損値NaNだけでなく無限大infも置換する。以下の記事を参照。

np.nan_to_num()の第一引数に配列ndarrayを指定すると、デフォルトでは欠損値が0に置換された新たなndarrayが生成される。元のndarrayは変更されない。

a = np.genfromtxt('data/src/sample_nan.csv', delimiter=',')
print(a)
# [[11. 12. nan 14.]
#  [21. nan nan 24.]
#  [31. 32. 33. 34.]]

print(np.nan_to_num(a))
# [[11. 12.  0. 14.]
#  [21.  0.  0. 24.]
#  [31. 32. 33. 34.]]

print(a)
# [[11. 12. nan 14.]
#  [21. nan nan 24.]
#  [31. 32. 33. 34.]]

第二引数copyFalseとすると元のndarrayが変更される。

np.nan_to_num(a, copy=False)
print(a)
# [[11. 12.  0. 14.]
#  [21.  0.  0. 24.]
#  [31. 32. 33. 34.]]

NumPy1.17以降は第三引数nanで置換する値を指定できる。

a = np.genfromtxt('data/src/sample_nan.csv', delimiter=',')
print(a)
# [[11. 12. nan 14.]
#  [21. nan nan 24.]
#  [31. 32. 33. 34.]]

print(np.nan_to_num(a, nan=-1))
# [[11. 12. -1. 14.]
#  [21. -1. -1. 24.]
#  [31. 32. 33. 34.]]

欠損値ではない要素の平均を算出する関数np.nanmean()を利用すると、平均値に置換可能。列・行ごとの平均値で置換することもできる。

print(np.nanmean(a))
# 23.555555555555557

print(np.nan_to_num(a, nan=np.nanmean(a)))
# [[11.         12.         23.55555556 14.        ]
#  [21.         23.55555556 23.55555556 24.        ]
#  [31.         32.         33.         34.        ]]

print(np.nanmean(a, axis=0, keepdims=True))
# [[21. 22. 33. 24.]]

print(np.nan_to_num(a, nan=np.nanmean(a, axis=0, keepdims=True)))
# [[11. 12. 33. 14.]
#  [21. 22. 33. 24.]
#  [31. 32. 33. 34.]]

print(np.nanmean(a, axis=1, keepdims=True))
# [[12.33333333]
#  [22.5       ]
#  [32.5       ]]

print(np.nan_to_num(a, nan=np.nanmean(a, axis=1, keepdims=True)))
# [[11.         12.         12.33333333 14.        ]
#  [21.         22.5        22.5        24.        ]
#  [31.         32.         33.         34.        ]]

np.nan_to_num()の第三引数nanndarrayを指定すると、第一引数のndarrayと同じ形状にブロードキャストされる。

np.nanmean()keepdims=Trueとすると正しくブロードキャストされて置換される。axis=0ではkeepdims=False(デフォルト)でも問題ないが、axisに関わらずkeepdims=Trueとしておいたほうが間違いは少ない。

1.17より前のバージョンでは引数nanが実装されていないので、0以外の値に置換したい場合は次に説明する方法を使う。

np.isnan()を利用して欠損値NaNを置換

欠損値NaNかを判定する関数np.isnan()を使って、欠損値の要素がTrueとなるndarrayを取得できる。

a = np.genfromtxt('data/src/sample_nan.csv', delimiter=',')
print(a)
# [[11. 12. nan 14.]
#  [21. nan nan 24.]
#  [31. 32. 33. 34.]]

print(np.isnan(a))
# [[False False  True False]
#  [False  True  True False]
#  [False False False False]]

これをマスクとして利用すると、欠損値の要素に任意の値を代入できる。

a[np.isnan(a)] = 0
print(a)
# [[11. 12.  0. 14.]
#  [21.  0.  0. 24.]
#  [31. 32. 33. 34.]]

np.nanmean()を使って平均値に置き換えることも可能。

a = np.genfromtxt('data/src/sample_nan.csv', delimiter=',')

a[np.isnan(a)] = np.nanmean(a)
print(a)
# [[11.         12.         23.55555556 14.        ]
#  [21.         23.55555556 23.55555556 24.        ]
#  [31.         32.         33.         34.        ]]

列・行ごとの平均値に置き換えたい場合はnp.where()を使う。

a = np.genfromtxt('data/src/sample_nan.csv', delimiter=',')

print(np.where(np.isnan(a), np.nanmean(a, axis=0, keepdims=True), a))
# [[11. 12. 33. 14.]
#  [21. 22. 33. 24.]
#  [31. 32. 33. 34.]]

print(np.where(np.isnan(a), np.nanmean(a, axis=1, keepdims=True), a))
# [[11.         12.         12.33333333 14.        ]
#  [21.         22.5        22.5        24.        ]
#  [31.         32.         33.         34.        ]]

関連カテゴリー

関連記事