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())

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 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"))

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 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}))

前方埋めと後方埋めは時系列データで特に有用で、データの連続性を保つことができます。どちらの方法を選ぶかは、データのビジネス上の意味によって決まります。


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"))

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 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())

よくある質問

1、fillna は新しいオブジェクトを作成するのか、それとも元のオブジェクトを変更するのか

fillnaデフォルトでは新しいオブジェクトを返します。inplace=Trueを使用すると、元のオブジェクトを変更できます。

2、補間後にも欠損値が残る場合

欠損値が系列の先頭または末尾にある場合、補間では埋めることができないため、追加の処理が必要です。

3、NaN と空文字列の区別

空文字列""は欠損値ではなく、replace("", np.nan)を使用して変換する必要があります。

埋め方を選ぶ際にはビジネス上の意味を考慮する必要があります。前方埋めは時系列データに適し、後方埋めは静的データに適し、平均値は数値型データに適し、補間は連続データに適しています。

その他の拡張