Pandas 欠損値処理
実データには欠損値(NaN)がよく存在します。Pandas は欠損データを処理するための豊富な関数を提供しています。このセクションでは、fillna、dropna、interpolate などのメソッドの使い方を詳しく説明します。
欠損値の表し方
Pandas ではNaN(Not a Number)は欠損値を表し、これは NumPy ライブラリに由来します。
例
import pandas as pd
import numpy as np
# 欠損値を含むデータを作成
s = pd.Series([1, 2, np.nan, 4, 5])
print("NaN を含む Series:")
print(s)
print(f"欠損値があるか: {s.isna().any()}")
print()
# DataFrame の欠損値
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [np.nan, 2, 3, 4],
"C": [1, 2, 3, np.nan]
})
print("欠損値を含む DataFrame:")
print(df)
print()
# 欠損値を検出
print("欠損値の位置:")
print(df.isna())
print()
# 各列の欠損値の数を集計
print("各列の欠損値の数:")
print(df.isna().sum())
import numpy as np
# 欠損値を含むデータを作成
s = pd.Series([1, 2, np.nan, 4, 5])
print("NaN を含む Series:")
print(s)
print(f"欠損値があるか: {s.isna().any()}")
print()
# DataFrame の欠損値
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [np.nan, 2, 3, 4],
"C": [1, 2, 3, np.nan]
})
print("欠損値を含む DataFrame:")
print(df)
print()
# 欠損値を検出
print("欠損値の位置:")
print(df.isna())
print()
# 各列の欠損値の数を集計
print("各列の欠損値の数:")
print(df.isna().sum())
Pandas では、
NaN、None、pandas.NAはすべて欠損値として認識されます。isna()或isnull()を使用すると、これらの欠損値を一括して検出できます。
dropna で欠損値を削除
行を削除
例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [1, np.nan, 3, 4],
"C": [1, 2, 3, np.nan]
})
print("元のデータ:")
print(df)
print()
# 欠損値を含む行を削除(デフォルト)
print("欠損値がある行を削除:")
print(df.dropna())
print()
# how='all':すべてが欠損値の場合のみ削除
print("すべてが欠損値の行のみ削除:")
print(df.dropna(how="all"))
print()
# thresh:非欠損値が少なくとも N 個ある場合のみ保持
print("非欠損値が少なくとも2つある場合のみ保持:")
print(df.dropna(thresh=2))
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [1, np.nan, 3, 4],
"C": [1, 2, 3, np.nan]
})
print("元のデータ:")
print(df)
print()
# 欠損値を含む行を削除(デフォルト)
print("欠損値がある行を削除:")
print(df.dropna())
print()
# how='all':すべてが欠損値の場合のみ削除
print("すべてが欠損値の行のみ削除:")
print(df.dropna(how="all"))
print()
# thresh:非欠損値が少なくとも N 個ある場合のみ保持
print("非欠損値が少なくとも2つある場合のみ保持:")
print(df.dropna(thresh=2))
列を削除
例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [np.nan, np.nan, np.nan, np.nan], # すべて欠損
"C": [1, 2, 3, 4]
})
print("元のデータ:")
print(df)
print()
# 欠損値を含む列を削除
print("欠損値がある列を削除:")
print(df.dropna(axis=1))
print()
# how='all':すべてが欠損値の列を削除
print("すべてが欠損値の列を削除:")
print(df.dropna(axis=1, how="all"))
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [np.nan, np.nan, np.nan, np.nan], # すべて欠損
"C": [1, 2, 3, 4]
})
print("元のデータ:")
print(df)
print()
# 欠損値を含む列を削除
print("欠損値がある列を削除:")
print(df.dropna(axis=1))
print()
# how='all':すべてが欠損値の列を削除
print("すべてが欠損値の列を削除:")
print(df.dropna(axis=1, how="all"))
fillna で欠損値を埋める
固定値で埋める
例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5],
"B": [np.nan, 2, 3, np.nan, 5],
"C": [1, 2, 3, 4, np.nan]
})
print("元のデータ:")
print(df)
print()
# 0 で埋める
print("0 で埋める:")
print(df.fillna(0))
print()
# 指定した値で異なる列を埋める
print("列ごとに異なる値で埋める:")
print(df.fillna({"A": 0, "B": 99, "C": -1}))
print()
# 前の値で埋める(前方埋め)
print("前方埋め:")
print(df.fillna(method="ffill"))
print()
# 後ろの値で埋める(後方埋め)
print("後方埋め:")
print(df.fillna(method="bfill"))
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5],
"B": [np.nan, 2, 3, np.nan, 5],
"C": [1, 2, 3, 4, np.nan]
})
print("元のデータ:")
print(df)
print()
# 0 で埋める
print("0 で埋める:")
print(df.fillna(0))
print()
# 指定した値で異なる列を埋める
print("列ごとに異なる値で埋める:")
print(df.fillna({"A": 0, "B": 99, "C": -1}))
print()
# 前の値で埋める(前方埋め)
print("前方埋め:")
print(df.fillna(method="ffill"))
print()
# 後ろの値で埋める(後方埋め)
print("後方埋め:")
print(df.fillna(method="bfill"))
統計値で埋める
例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5, 6],
"B": [10, np.nan, 30, np.nan, 50, 60]
})
print("元のデータ:")
print(df)
print()
# 平均値で埋める
print("平均値で埋める:")
print(df.fillna(df.mean()))
print()
# 中央値で埋める
print("中央値で埋める:")
print(df.fillna(df.median()))
print()
# 列ごとに個別に埋める
print("A列は平均値、B列は0で埋める:")
print(df.fillna({"A": df["A"].mean(), "B": 0}))
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5, 6],
"B": [10, np.nan, 30, np.nan, 50, 60]
})
print("元のデータ:")
print(df)
print()
# 平均値で埋める
print("平均値で埋める:")
print(df.fillna(df.mean()))
print()
# 中央値で埋める
print("中央値で埋める:")
print(df.fillna(df.median()))
print()
# 列ごとに個別に埋める
print("A列は平均値、B列は0で埋める:")
print(df.fillna({"A": df["A"].mean(), "B": 0}))
前方埋めと後方埋めは時系列データで特に有用で、データの連続性を保つことができます。どちらの方法を選ぶかは、データのビジネス上の意味によって決まります。
interpolate で補間して埋める
補間はよりスマートな埋め方で、隣接するデータから欠損値を推定できます。
線形補間
例
import pandas as pd
import numpy as np
s = pd.Series([1, 2, np.nan, 4, 5, np.nan, 7])
print("元の Series:")
print(s)
print()
# 線形補間(デフォルト)
print("線形補間:")
print(s.interpolate())
print()
# 補間方法を指定
print("time 加重補間(時系列):")
s2 = pd.Series([1, np.nan, np.nan, 4], index=pd.date_range("2024-01-01", periods=4, freq="D"))
print(s2.interpolate(method="time"))
print()
# 近傍値で埋める
print("近傍値で埋める:")
print(s.interpolate(method="nearest"))
import numpy as np
s = pd.Series([1, 2, np.nan, 4, 5, np.nan, 7])
print("元の Series:")
print(s)
print()
# 線形補間(デフォルト)
print("線形補間:")
print(s.interpolate())
print()
# 補間方法を指定
print("time 加重補間(時系列):")
s2 = pd.Series([1, np.nan, np.nan, 4], index=pd.date_range("2024-01-01", periods=4, freq="D"))
print(s2.interpolate(method="time"))
print()
# 近傍値で埋める
print("近傍値で埋める:")
print(s.interpolate(method="nearest"))
DataFrame の補間
例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5],
"B": [10, np.nan, 30, 40, np.nan]
})
print("元のデータ:")
print(df)
print()
# 元の DataFrame に補間を適用
df_interpolated = df.interpolate(method="linear")
print("線形補間後:")
print(df_interpolated)
print()
# 補間範囲を制限
print("連続する欠損値の先頭と末尾のみを埋める(最大1個):")
print(df.interpolate(limit=1))
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5],
"B": [10, np.nan, 30, 40, np.nan]
})
print("元のデータ:")
print(df)
print()
# 元の DataFrame に補間を適用
df_interpolated = df.interpolate(method="linear")
print("線形補間後:")
print(df_interpolated)
print()
# 補間範囲を制限
print("連続する欠損値の先頭と末尾のみを埋める(最大1個):")
print(df.interpolate(limit=1))
実践:実データの処理
例
import pandas as pd
import numpy as np
# 実際のビジネスデータを模擬する
np.random.seed(42)
n = 20
df = pd.DataFrame({
"日付": pd.date_range("2024-01-01", periods=n),
"売上高": np.random.choice([100, 200, np.nan, 300, np.nan], n),
"顧客数": np.random.choice([10, np.nan, 20, 30], n),
"コンバージョン率": np.random.choice([0.05, 0.1, np.nan, 0.15], n)
})
print("元のデータの欠損状況:")
print(df.isna().sum())
print()
# 処理方針:
# 1. 売上高は前後の平均値で埋める(業務上、変動が許容されるため)
df["売上高"] = df["売上高"].interpolate()
# 2. 顧客数は平均値で埋める
df["顧客数"] = df["顧客数"].fillna(df["顧客数"].mean())
# 3. コンバージョン率は0で埋める(未変換を表す)
df["コンバージョン率"] = df["コンバージョン率"].fillna(0)
print("処理後のデータ:")
print(df)
print()
print("処理後の欠損状況:")
print(df.isna().sum())
import numpy as np
# 実際のビジネスデータを模擬する
np.random.seed(42)
n = 20
df = pd.DataFrame({
"日付": pd.date_range("2024-01-01", periods=n),
"売上高": np.random.choice([100, 200, np.nan, 300, np.nan], n),
"顧客数": np.random.choice([10, np.nan, 20, 30], n),
"コンバージョン率": np.random.choice([0.05, 0.1, np.nan, 0.15], n)
})
print("元のデータの欠損状況:")
print(df.isna().sum())
print()
# 処理方針:
# 1. 売上高は前後の平均値で埋める(業務上、変動が許容されるため)
df["売上高"] = df["売上高"].interpolate()
# 2. 顧客数は平均値で埋める
df["顧客数"] = df["顧客数"].fillna(df["顧客数"].mean())
# 3. コンバージョン率は0で埋める(未変換を表す)
df["コンバージョン率"] = df["コンバージョン率"].fillna(0)
print("処理後のデータ:")
print(df)
print()
print("処理後の欠損状況:")
print(df.isna().sum())
よくある質問
1、fillna は新しいオブジェクトを作成するのか、それとも元のオブジェクトを変更するのか
fillnaデフォルトでは新しいオブジェクトを返します。inplace=Trueを使用すると、元のオブジェクトを変更できます。
2、補間後にも欠損値が残る場合
欠損値が系列の先頭または末尾にある場合、補間では埋めることができないため、追加の処理が必要です。
3、NaN と空文字列の区別
空文字列""は欠損値ではなく、replace("", np.nan)を使用して変換する必要があります。
その他の拡張埋め方を選ぶ際にはビジネス上の意味を考慮する必要があります。前方埋めは時系列データに適し、後方埋めは静的データに適し、平均値は数値型データに適し、補間は連続データに適しています。