pandas

【pandas】欠損値(NaN、None)の扱いと処理方法 ~ isnull, notnull, dropna, fillna ~

【pandas】欠損値(NaN、None)の扱いと処理方法 _ isnull, notnull, dropna, fillna _

pandasの欠損値(NaN、None)の扱いと処理方法の基本について解説します。

pandasにおける欠損値(NaN、None)の扱い

データ分析をしている際には何かしらデータが欠損しているようなデータを扱う場合があります。例えば、製造業では設備データの取得などでデータが取れていない時刻があるといったケースはよくあるでしょう。

このような際に事前に適切に欠損値を処理しておくことが、後の分析の質を高めるためには重要です。pandasでデータ分析をしている場合には、NaNNoneといったデータをどう扱うかが重要になってきます。

本記事では、pandasで欠損値であるNaNNoneを検出する方法(isnull, notnull)や、欠損値を処理するための各種メソッド(dropna, fillna)について紹介します。

まずは、pandasにおいて欠損値がどのように扱われるかの概要について紹介し、その後に欠損値を扱う方法例について説明していきます

pandasでの欠損値の扱い

pandasの欠損値は、NumPyのNaNnp.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

上記の例では、定義の際にNaNnp.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の位置で欠損している箇所が分かります。

Note

isnullメソッドの公式ドキュメントについて、Seriesはこちら、DataFrameはこちらを参照してください。

欠損値でないことを判定する ~ 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の位置で欠損していない箇所が分かります。

Note

notnullメソッドの公式ドキュメントについて、Seriesはこちら、DataFrameはこちらを参照してください。

欠損値の処理方法

欠損値を具体的にどのように処理するかを見ていきます。具体的には、以下の2つの対処方法について例を使って紹介します。

  1. 欠損値の削除方法 dropna
  2. 欠損値を指定した値で埋める方法 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つ以上ある列を残して、他の行または列を削除します。

Note

dropnaメソッドの公式ドキュメントについて、Seriesはこちら、DataFrameはこちらを参照してください。

欠損値を指定した値で埋める ~ 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となっている場合には値が埋められないことに注意しましょう。

Note

fillnaメソッドの公式ドキュメントについて、Seriesはこちら、DataFrameはこちらを参照してください。

まとめ

pandasの欠損値(NaN、None)の扱いと処理方法の基本について解説しました。

欠損値を検出するためのisnullやnotnullといったメソッドを紹介し、具体的に欠損値を処理の際に、削除する(dropna)、指定値で埋める(fillna)といったメソッドの使い方を例を使って紹介しています。

データ分析の際に何かしらのデータが欠損しているという状況はよく直面します。例えば、製造業では設備データの取得などでデータが取れていない時刻があるといったケースはよくあるでしょう。このような際に事前に適切に欠損値を処理しておくことが、後の分析の質を高めるためには重要です。

また、どういった欠損値処理をするのが適切かは問題により異なりますので十分に検討するようにしてください。pandasにおける欠損値処理方法を理解し、うまくデータ分析を進めてもらえればと思います。