pandasの欠損値(NaN、None)の扱いと処理方法の基本について解説します。
pandasにおける欠損値(NaN、None)の扱い
データ分析をしている際には何かしらデータが欠損しているようなデータを扱う場合があります。例えば、製造業では設備データの取得などでデータが取れていない時刻があるといったケースはよくあるでしょう。
このような際に事前に適切に欠損値を処理しておくことが、後の分析の質を高めるためには重要です。pandasでデータ分析をしている場合には、NaN
やNone
といったデータをどう扱うかが重要になってきます。
本記事では、pandasで欠損値であるNaN
やNone
を検出する方法(isnull
, notnull
)や、欠損値を処理するための各種メソッド(dropna
, fillna
)について紹介します。
まずは、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
を混ぜたリストを使ってSeriesやDataFrameを定義をしていますが、結果を見るとわかるようにfloat64
の浮動小数点にアップキャストされていることが分かります。
pandasのSeriesやDataFrameを使う際に、PythonのNone
を含んだデータを指定することができますが、pandasでは浮動小数点にキャストする際にNone
を自動的に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
となっているブール値のマスクが返却されますので、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
となっているブール値のマスクが返却されますので、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
となっている場合には値が埋められないことに注意しましょう。
まとめ
pandasの欠損値(NaN
、None
)の扱いと処理方法の基本について解説しました。
欠損値を検出するためのisnull
やnotnull
といったメソッドを紹介し、具体的に欠損値を処理の際に、削除する(dropna
)、指定値で埋める(fillna
)といったメソッドの使い方を例を使って紹介しています。
データ分析の際に何かしらのデータが欠損しているという状況はよく直面します。例えば、製造業では設備データの取得などでデータが取れていない時刻があるといったケースはよくあるでしょう。このような際に事前に適切に欠損値を処理しておくことが、後の分析の質を高めるためには重要です。
また、どういった欠損値処理をするのが適切かは問題により異なりますので十分に検討するようにしてください。pandasにおける欠損値処理方法を理解し、うまくデータ分析を進めてもらえればと思います。
上記で紹介しているソースコードについてはgithubにて公開しています。参考にしていただければと思います。