pandasの欠損値(NaN、None)の扱いと処理方法の基本について解説します。
pandasにおける欠損値(NaN、None)の扱い
データ分析をしている際には何かしらデータが欠損しているようなデータを扱う場合があります。欠損値は分析のミスにつながってしまうため、適切に前処理してから集計などの処理を実施するべきです。
まずは、pandasにおいて欠損値がどのように扱われるかの概要について紹介し、その後に欠損値を扱う方法例について説明します。
pandasでの欠損値の扱い
pandasの欠損値は、NumPyのNaN(np.nan)とPythonのNoneで表現することができます。なお、NaNは、IEEE浮動小数点表現を使用するシステムで認識される特殊な浮動小数点数値です。
以下がpandasでの欠損値の簡単な例です。
import numpy as np import pandas as pd # Seriesでの欠損値の例 ser = pd.Series([1, np.nan, 3, None, 5]) print(ser, '\n') # DataFrameでの欠損値の例 df = pd.DataFrame([[1, np.nan, 3], [4, 5, None]], index=[1, 2], columns=['attr1', 'attr2', 'attr3']) print(df, '\n') print(df.dtypes)
【実行結果】 0 1.0 1 NaN 2 3.0 3 NaN 4 5.0 dtype: float64 attr1 attr2 attr3 1 1 NaN 3.0 2 4 5.0 NaN attr1 int64 attr2 float64 attr3 float64 dtype: object
上記の例では、定義の際にNaN(np.nan)とNoneを混ぜた形で定義をしていますが、結果を見てもわかるように、結果はfloat64の浮動小数点にアップキャストされています。
PythonのNoneを指定することができますが、pandasでは浮動小数点にキャストする際にNoneを自動的にNaNに変換し、上記のように欠損値をNaNとして扱います。
以降では、SeriesやDataFrameで欠損値を扱うための以下メソッドについて例を使って紹介します。
- 欠損値を検出する方法:isnull, notnull
- 欠損値を含む行または列を削除する方法:dropna
- 欠損値を指定した値で埋める方法:fillna
欠損値の検出方法
対象データに欠損値が含まれるかを検出するための方法を見ていきます。pandasで欠損値の検出を行う際には、isnullメソッドやnotnullメソッドを使用します。
欠損値であることを判定する ~ isnull ~
欠損値であること判定するには、以下のようにisnullメソッドを使用します。
import numpy as np import pandas as pd # Series ser = pd.Series([1, np.nan, 3, None, 5]) print(ser.isnull(), '\n') # DataFrame df = pd.DataFrame([[1, np.nan, 3], [4, 5, None]], index=[1, 2], columns=['attr1', 'attr2', 'attr3']) print(df.isnull())
【実行結果】 0 False 1 True 2 False 3 True 4 False dtype: bool attr1 attr2 attr3 1 False True False 2 False False True
上記結果のように、欠損値である位置がTrueとなっているブール値のマスクが返却されます。
欠損値でないことを判定する ~ notnull ~
欠損値でないことを判定するには、以下のようにnotnullメソッドを使用します。
import numpy as np import pandas as pd # Series ser = pd.Series([1, np.nan, 3, None, 5]) print(ser.notnull(), '\n') # DataFrame df = pd.DataFrame([[1, np.nan, 3], [4, 5, None]], index=[1, 2], columns=['attr1', 'attr2', 'attr3']) print(df.notnull())
【実行結果】 0 True 1 False 2 True 3 False 4 True dtype: bool attr1 attr2 attr3 1 True False True 2 True True False
上記結果のように、欠損値ではない位置がTrueとなっているブール値のマスクが返却されます。
欠損値の処理方法
欠損値を具体的にどのように処理するかを見ていきます。具体的には、以下の2つの対処方法について例を使って紹介します。
- 欠損値の削除方法 dropna
- 欠損値を指定した値で埋める方法 fillna
欠損値の削除方法 ~ dropna ~
基本的な使い方
pandasで欠損値を削除する際には、以下のようにdropnaメソッドを使用します。
import numpy as np import pandas as pd # Series ser = pd.Series([1, np.nan, 3, None, 5]) print(ser.dropna(), '\n') # DataFrame df = pd.DataFrame([[1, np.nan, 3], [4, 5, None], [7, 8, 9]], index=[1, 2, 3], columns=['attr1', 'attr2', 'attr3']) # 行方向の削除 print(df.dropna(), '\n') # 列方向の削除 print(df.dropna(axis=1))
【実行結果】 0 1.0 2 3.0 4 5.0 dtype: float64 attr1 attr2 attr3 3 7 8.0 9.0 attr1 1 1 2 4 3 7
DataFrameの場合は、axisにより削除の方向を指定することができます。
デフォルトでは行方向のaxis=0となっており、列方向を指定する場合には、axis=1とします。
全て欠損値の場合に削除する how
上記で紹介した削除は欠損値を含む行または列を削除しました。場合によっては、全てが欠損値である行または列を取り除くといったことをしたくなります。
dropnaでは、デフォルトではhow=’any’となっているため、欠損値を含む行または列が削除されます。全ての値が欠損値の行または列を削除したい場合には、以下のようにhow=’all’を指定します。
import numpy as np import pandas as pd # DataFrame df = pd.DataFrame([[1, 2, np.nan], [np.nan, np.nan, np.nan], [7, 8, np.nan]], index=[1, 2, 3], columns=['attr1', 'attr2', 'attr3']) # 行方向の削除(全てNaNの行のみ削除) print(df.dropna(how='all'), '\n') # 列方向の削除(全てNaNの列のみ削除) print(df.dropna(axis=1, how='all'))
【実行結果】 attr1 attr2 attr3 1 1.0 2.0 NaN 3 7.0 8.0 NaN attr1 attr2 1 1.0 2.0 2 NaN NaN 3 7.0 8.0
上記のようにhow=’all’を指定することで、すべての値がNaNである行また列を削除できていることが分かります。
欠損値以外のデータ数で削除を制御する thresh
指定した数字以上の値(欠損値以外)を含む行または列を除いて削除する場合には、threshで欠損値以外であるデータの数を指定します。
import numpy as np import pandas as pd # DataFrame df = pd.DataFrame([[1, 2, 3, np.nan], [5, 6, np.nan, 8], [np.nan, 10, np.nan, 12], [np.nan, np.nan, np.nan, 16]], index=[1, 2, 3, 4], columns=['attr1', 'attr2', 'attr3', 'attr4']) # 行方向の削除 (欠損値以外が指定数値以上ある行を残す) print(df.dropna(thresh=3), '\n') # 列方向の削除 (欠損値以外が指定数値以上ある行を残す) print(df.dropna(axis=1, thresh=3))
【実行結果】 attr1 attr2 attr3 attr4 1 1.0 2.0 3.0 NaN 2 5.0 6.0 NaN 8.0 attr2 attr4 1 2.0 NaN 2 6.0 8.0 3 10.0 12.0 4 NaN 16.0
上記はthresh=3とした場合の結果例です。この場合は、行方向の場合は欠損値以外の値が3つ以上ある行を、列方向(axis=1)の場合は、欠損値以外の値が3つ以上ある列を残して、他の行または列を削除します。
欠損値を指定した値で埋める ~ fillna ~
基本的な使用方法
欠損値を指定した値で埋めたい場合には、以下のようにfillnaメソッドを使用します。
import numpy as np import pandas as pd # Series ser = pd.Series([1, 2, np.nan, np.nan, 5]) # 欠損値を指定した値で埋める print(ser.fillna(0), '\n') # DataFrame df = pd.DataFrame([[1, 2, 3, np.nan], [5, np.nan, np.nan, 8], [np.nan, 10, np.nan, 12], [np.nan, np.nan, np.nan, 16]], index=[1, 2, 3, 4], columns=['attr1', 'attr2', 'attr3', 'attr4']) # 欠損値を指定した値で埋める print(df.fillna(0))
【実行結果】 0 1.0 1 2.0 2 0.0 3 0.0 4 5.0 dtype: float64 attr1 attr2 attr3 attr4 1 1.0 2.0 3.0 0.0 2 5.0 0.0 0.0 8.0 3 0.0 10.0 0.0 12.0 4 0.0 0.0 0.0 16.0
上記の例では、欠損値を0で埋めている例です。合計などで欠損値部分を無視したい場合などには0で埋める場合が多いかと思います。
フォワードで欠損値を埋める ffill
元のデータの前の値を使って欠損値を埋める場合には、fillnaメソッドでmethod=’ffill’を指定します。前方から後方に埋めていくことからフォワードフィルでffillという名前となっています。
import numpy as np import pandas as pd # Series ser = pd.Series([1, 2, np.nan, np.nan, 5]) # フォワードで欠損値を埋める print(ser.fillna(method='ffill'), '\n') # DataFrame df = pd.DataFrame([[1, 2, 3, np.nan], [5, np.nan, np.nan, 8], [np.nan, 10, np.nan, 12], [np.nan, np.nan, np.nan, 16]], index=[1, 2, 3, 4], columns=['attr1', 'attr2', 'attr3', 'attr4']) # フォワードで欠損値を埋める (行方向) print(df.fillna(method='ffill'), '\n') # フォワードで欠損値を埋める (列方向) print(df.fillna(axis=1, method='ffill'))
【実行結果】 0 1.0 1 2.0 2 2.0 3 2.0 4 5.0 dtype: float64 attr1 attr2 attr3 attr4 1 1.0 2.0 3.0 NaN 2 5.0 2.0 3.0 8.0 3 5.0 10.0 3.0 12.0 4 5.0 10.0 3.0 16.0 attr1 attr2 attr3 attr4 1 1.0 2.0 3.0 3.0 2 5.0 5.0 5.0 8.0 3 NaN 10.0 10.0 12.0 4 NaN NaN NaN 16.0
DataFrameの場合は、axisで値を埋める方向が決まります。前の値を埋めることから、最初の値がNaNとなっている場合には値が埋められないことに注意しましょう。
バックワードで欠損値を埋める bfill
元のデータの後の値を使って欠損値を埋める場合には、fillnaメソッドでmethod=’bfill’を指定します。後方から前方に埋めていくことからバックワードフィルでbfillという名前となっています。
import numpy as np import pandas as pd # Series ser = pd.Series([1, 2, np.nan, np.nan, 5]) # バックワードで欠損値を埋める print(ser.fillna(method='bfill'), '\n') # DataFrame df = pd.DataFrame([[1, 2, 3, np.nan], [5, np.nan, np.nan, 8], [np.nan, 10, np.nan, 12], [np.nan, np.nan, np.nan, 16]], index=[1, 2, 3, 4], columns=['attr1', 'attr2', 'attr3', 'attr4']) # バックワードで欠損値を埋める (行方向) print(df.fillna(method='bfill'), '\n') # バックワードで欠損値を埋める (列方向) print(df.fillna(axis=1, method='bfill'))
【実行結果】 0 1.0 1 2.0 2 5.0 3 5.0 4 5.0 dtype: float64 attr1 attr2 attr3 attr4 1 1.0 2.0 3.0 8.0 2 5.0 10.0 NaN 8.0 3 NaN 10.0 NaN 12.0 4 NaN NaN NaN 16.0 attr1 attr2 attr3 attr4 1 1.0 2.0 3.0 NaN 2 5.0 8.0 8.0 8.0 3 10.0 10.0 12.0 12.0 4 16.0 16.0 16.0 16.0
DataFrameの場合は、axisで値を埋める方向が決まります。後の値を埋めることから、最後の値がNaNとなっている場合には値が埋められないことに注意しましょう。
上記で紹介しているソースコードについてはgithubにて公開しています。参考にしていただければと思います。