【Pandas】欠損値の処理方法【isnull・dropna・fillna・interpolate】
欠損値(NaN)はデータクリーニングにおける最大の敵です。実際のデータが完璧であることはまずありません。センサーの故障で温度データが欠落したり、ユーザーが任意項目をスキップして年齢が欠損したり、システムのマージでIDが消えたりします。Pandasは、欠損値の検出・削除・補完・補間に関する完全なツールチェーンを提供しています。本セクションでは、それぞれの方法を順に解説し、Copy-on-Write環境で安全に操作する方法を説明します。
⚠️ 注意: 以下のコードはローカルのPython環境で実行してください。
1. 学習内容
- ❶ NaNの本質とセマンティクス
- ❷ isnull / notnullによる検出
- ❸ dropnaによる欠損行・欠損列の削除
- ❹ fillnaによる欠損値の補完
- ❺ interpolateによる補間メソッド
2. キャロルのワークアウトデータに見る欠損
(1) 課題:心拍数データの30%が欠損している
キャロルのフィットネストラッカーは30日間の心拍数データを記録しましたが、そのうち9日間はワークアウト中にシグナルが途切れ(NaN)、データが欠損しています:
PYTHON
import pandas as pd
import numpy as np
hr = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=30),
'heart_rate': [72, 75, np.nan, 78, np.nan, 80, 82, np.nan,
85, 88, np.nan, 90, 87, np.nan, 83, 80,
np.nan, 78, 76, np.nan, 74, 72, np.nan, 70,
68, np.nan, 65, 63, np.nan, 60]
})
print(f"欠損数: {hr['heart_rate'].isnull().sum()} / {len(hr)}")
# 欠損数: 9 / 30
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
(2) 解決策:4つの戦略とそれぞれの用途
| 戦略 | メソッド | 使用場面 |
|---|---|---|
| 検出 | isnull / notnull | 欠損値の分布を把握する |
| 削除 | dropna | 欠損が少なく、行が重要でない場合 |
| 補完 | fillna | 妥当なデフォルト値が存在する場合 |
| 補間 | interpolate | 明確なトレンドを持つ時系列データ |
3. NaNの本質
(1) NaNはNaNと等しくない
▶ サンプル:NaNの特殊な挙動(難易度 ⭐)
PYTHON
import numpy as np
# NaNは自分自身と等しくない — これはIEEE 754規格の仕様です
print(np.nan == np.nan) # False!
print(np.nan != np.nan) # True!
# NaNの検出にはnp.isnan()を使用します
print(np.isnan(np.nan)) # True
# Pandasは内部でこれを処理します — isnull()は正しく動作します
import pandas as pd
s = pd.Series([1, np.nan, 3, None, 5])
print(s.isnull())
# 0 False
# 1 True
# 2 False
# 3 True
# 4 False
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
(2) Pandasにおける欠損値の型
| 型 | 欠損の表現 | 例 |
|---|---|---|
| float64 | np.nan | デフォルトの浮動小数点欠損値 |
| int64 | NaNをネイティブに表現できない → 自動的にfloat64に変換 | [1, None, 3] → float64 |
| object | None / np.nan | 混合型の列 |
| string | pd.NA | Pandas 1.x以降のnullable文字列 |
| Int64 | pd.NA | nullable整数型(大文字のI) |
| datetime64 | NaT | 欠損した時間値 |
💡 ヒント: Pandas 2.xのNullable型(
Int64、boolean、stringなど大文字で始まる型)は、np.nanの代わりにpd.NAを使用し、整数型で欠損値を表現できない問題を解決しています。変換方法:df['col'].astype('Int64')。
4. isnull / notnullによる検出
(1) 基本的な検出
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:欠損値の検出(難易度 ⭐)
PYTHON
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['アリス', 'ボブ', 'チャーリー', 'キャロル', 'デビッド'],
'age': [28, np.nan, 25, 30, np.nan],
'salary': [75000, 92000, np.nan, 88000, 105000],
'department': ['営業', 'エンジニアリング', np.nan, '営業', '経営']
})
# 要素ごとの検出
print(df.isnull())
# name age salary department
# 0 False False False False
# 1 False True False False
# 2 False False True True
# 3 False False False False
# 4 False True False False
# 列ごとの欠損数
print(df.isnull().sum())
# name 0
# age 2
# salary 1
# department 1
# 総欠損数
print(f"総欠損数: {df.isnull().sum().sum()}") # 4
# notnull(逆の判定)
print(df.notnull().sum()) # 列ごとの非欠損数
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
(2) 欠損値の可視化と分析
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:欠損パターンの分析(難易度 ⭐⭐)
PYTHON
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['アリス', 'ボブ', 'チャーリー', 'キャロル', 'デビッド', 'イヴ'],
'age': [28, np.nan, 25, 30, np.nan, 27],
'salary': [75000, 92000, np.nan, 88000, 105000, np.nan],
'department': ['営業', 'エンジニアリング', np.nan, '営業', '経営', '人事']
})
# 欠損を含む行はどれか?
rows_with_missing = df[df.isnull().any(axis=1)]
print(f"欠損値を含む行数: {len(rows_with_missing)}")
# 3行(ボブ、チャーリー、デビッド)
# 欠損を含む列はどれか?
cols_with_missing = df.columns[df.isnull().any()].tolist()
print(f"欠損を含む列: {cols_with_missing}")
# ['age', 'salary', 'department']
# 列ごとの欠損率
missing_pct = (df.isnull().sum() / len(df) * 100).round(1)
print(missing_pct[missing_pct > 0])
# age 33.3%
# salary 33.3%
# department 16.7%
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
5. dropnaによる欠損値の削除
(1) 基本的な削除
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:dropnaによる削除(難易度 ⭐)
PYTHON
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['アリス', 'ボブ', 'チャーリー', 'キャロル', 'デビッド'],
'age': [28, np.nan, 25, 30, np.nan],
'salary': [75000, 92000, np.nan, 88000, 105000]
})
# 欠損を含む行を削除(デフォルト)
print(df.dropna())
# name age salary
# 0 アリス 28.0 75000.0
# 3 キャロル 30.0 88000.0
# すべての値が欠損の行のみ削除
print(df.dropna(how='all'))
# 欠損を含む列を削除
print(df.dropna(axis=1))
# name
# 0 アリス
# 1 ボブ
# ...
# しきい値付きで削除(非欠損値がN個以上の行を保持)
print(df.dropna(thresh=2)) # 非NaN値が少なくとも2個必要
# name age salary
# 0 アリス 28.0 75000.0
# 1 ボブ NaN 92000.0
# 3 キャロル 30.0 88000.0
# 4 デビッド NaN 105000.0
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
(2) dropnaのパラメータ詳細
| パラメータ | デフォルト | 説明 |
|---|---|---|
| axis | 0 | 0 = 行を削除、1 = 列を削除 |
| how | 'any' | 'any' = 1つでもNaNがあれば削除、'all' = すべてNaNの場合のみ削除 |
| thresh | None | 非欠損値が少なくともN個ある行を保持 |
| subset | None | 指定した列のみをチェック |
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:dropnaによる列指定チェック(難易度 ⭐⭐)
PYTHON
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['アリス', 'ボブ', 'チャーリー', 'キャロル', 'デビッド'],
'age': [28, np.nan, 25, 30, np.nan],
'salary': [75000, 92000, np.nan, 88000, 105000],
'department': ['営業', 'エンジニアリング', 'マーケティング', '営業', '経営']
})
# 'salary'が欠損の行のみ削除
# ('age'が欠損の行は保持 — 年齢は補完できるため)
print(df.dropna(subset=['salary']))
# name age salary department
# 0 アリス 28.0 75000.0 営業
# 1 ボブ NaN 92000.0 エンジニアリング
# 3 キャロル 30.0 88000.0 営業
# 4 デビッド NaN 105000.0 経営
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
6. fillnaによる欠損値の補完
(1) 定数による補完
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:fillnaによる定数・統計値での補完(難易度 ⭐⭐)
PYTHON
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['アリス', 'ボブ', 'チャーリー', 'キャロル', 'デビッド'],
'age': [28, np.nan, 25, 30, np.nan],
'salary': [75000, 92000, np.nan, 88000, 105000]
})
# 定数で補完
df_const = df.fillna({'age': 0, 'salary': 0})
print(df_const)
# 平均値で補完(数値データで最も一般的)
df_mean = df.copy()
df_mean['age'] = df_mean['age'].fillna(df_mean['age'].mean())
df_mean['salary'] = df_mean['salary'].fillna(df_mean['salary'].median())
print(df_mean)
# age: 27.67で補完、salary: 88000.0で補完
# 前方補完(前の値を使用)
df_ffill = df.fillna(method='ffill')
print(df_ffill)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
⚠️ 注意: Pandas 2.xでは、
fillna(method=...)は非推奨です。代わりにdf.ffill() / df.bfill()を使用してください。
(2) 前方補完 / 後方補完
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:ffillとbfill(難易度 ⭐)
PYTHON
import pandas as pd
import numpy as np
# 時系列データ — ここではffillが有効です
ts = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=8),
'temperature': [22.5, np.nan, np.nan, 23.1, np.nan, 24.0, np.nan, 24.5]
})
# 前方補完(直前の有効値を伝播)
print(ts.assign(temperature_ffill=ts['temperature'].ffill()))
# NaN → 直前の非NaN値を使用
# 後方補完(直後の有効値を伝播)
print(ts.assign(temperature_bfill=ts['temperature'].bfill()))
# NaN → 直後の非NaN値を使用
# 制限:連続するNaNをN個まで補完
print(ts.assign(temperature_limited=ts['temperature'].ffill(limit=1)))
# 連続するNaNを1個のみ補完し、残りはそのまま
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
7. interpolateによる補間
(1) 線形補間
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:interpolateによる線形補間(難易度 ⭐⭐)
PYTHON
import pandas as pd
import numpy as np
# キャロルの心拍数データ — 線形補間が自然です
hr = pd.DataFrame({
'time_min': list(range(10)),
'heart_rate': [72, 75, np.nan, 78, np.nan, 80, 82, np.nan, 85, 88]
})
# 線形補間(デフォルト)
hr['hr_linear'] = hr['heart_rate'].interpolate()
print(hr[['time_min', 'heart_rate', 'hr_linear']])
# 時刻2: (75+78)/2 = 76.5
# 時刻4: (78+80)/2 = 79.0
# 時刻7: (82+85)/2 = 83.5
# インデックスベースと時間ベースの補間
hr_ts = pd.DataFrame({
'heart_rate': [72, 75, np.nan, 78, np.nan, 80]
}, index=pd.to_timedelta([0, 5, 15, 20, 40, 45], unit='min'))
# 時間加重補間
hr_ts['hr_time'] = hr_ts['heart_rate'].interpolate(method='time')
print(hr_ts)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
(2) 補間メソッドの比較
| メソッド | method | 使用場面 |
|---|---|---|
| 線形補間 | 'linear' | 等間隔サンプリングデータ、滑らかなトレンド |
| 時間加重 | 'time' | 不等間隔の時間データ |
| インデックス加重 | 'index' | 不等間隔のインデックス |
| 2次補間 | 'quadratic' | 曲線的なトレンド |
| 3次補間 | 'cubic' | 滑らかな曲線 |
| 最近傍 | 'nearest' | 離散 / ステップデータ |
| スプライン | 'spline' | 滑らかな補間(orderパラメータが必要) |
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:補間メソッドの比較(難易度 ⭐⭐)
PYTHON
import pandas as pd
import numpy as np
s = pd.Series([0.0, np.nan, np.nan, np.nan, np.nan, 5.0, 10.0])
print("linear: ", s.interpolate(method='linear').tolist())
# [0.0, ~1.67, ~3.33, 5.0, ~6.67, 5.0, 10.0]
# ただしNaNの境界を含みます。
print("linear (trim):", s.dropna().interpolate(method='linear').tolist())
# 線形補間のみを示します。
# quadraticは境界の両側に少なくとも1つの非NaN値が必要です。ここでは7つのデータポイントを使用
print("quadratic:", s.interpolate(method='quadratic').tolist())
# 2次曲線をフィット。少なくとも3つの非NaNポイントが必要
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
8. Copy-on-Writeと欠損値操作
(1) CoW環境での安全な書き込み
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:CoW環境での安全な操作(難易度 ⭐⭐)
PYTHON
import pandas as pd
import numpy as np
pd.options.mode.copy_on_write = True # Pandas 3.xのデフォルト
df = pd.DataFrame({
'name': ['アリス', 'ボブ', 'チャーリー'],
'age': [28, np.nan, 25],
'salary': [75000, 92000, np.nan]
})
# ✅ 安全:結果を代入で戻す
df['age'] = df['age'].fillna(df['age'].mean())
# ✅ 安全:inplaceを使用(CoWでも動作)
df.fillna({'salary': 0}, inplace=True)
# ❌ 危険(CoW以前):チェーン代入
# subset = df[df['age'].notnull()]
# subset['salary'] = 100 # dfが変更されない可能性あり!
# ✅ CoWで安全:locを使用
df.loc[df['age'].notnull(), 'salary'] = df.loc[df['age'].notnull(), 'salary'] * 1.1
print(df)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
9. 総合例:欠損値処理の完全ワークフロー
(5) ▶ 欠損値処理の判断ツリー
graph TB
A[欠損値を検出] --> B{欠損率は?}
B -->|5%未満| C[dropnaで削除]
B -->|5〜30%| D{データ型は?}
D -->|数値型| E[fillnaで平均/中央値を補完]
D -->|カテゴリ型| F[fillnaで最頻値を補完]
D -->|時系列| G[interpolateで補間]
B -->|50%超| H[列の削除を検討]
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
:欠損値クリーニングの完全ワークフロー(難易度 ⭐⭐⭐)
PYTHON
import pandas as pd
import numpy as np
# ============================================
# 総合例:欠損値処理の完全パイプライン
# ============================================
# 1. 欠損値を含むデータを読み込む
np.random.seed(42)
df = pd.DataFrame({
'customer_id': range(1, 21),
'name': [f'Customer_{i}' for i in range(1, 21)],
'age': [25, np.nan, 35, 40, np.nan, 28, np.nan, 50, 33, np.nan,
29, np.nan, 45, 38, np.nan, 27, np.nan, 42, 31, np.nan],
'purchase_amount': [120, 85, np.nan, 200, 150, np.nan, 90, 310,
np.nan, 60, 175, np.nan, 250, 140, np.nan,
95, 180, np.nan, 110, 75],
'category': ['Electronics', np.nan, 'Home', 'Electronics', 'Clothing',
'Home', np.nan, 'Electronics', 'Clothing', np.nan,
'Home', 'Electronics', 'Clothing', np.nan, 'Home',
'Electronics', np.nan, 'Home', 'Clothing', 'Electronics']
})
# 2. 欠損パターンを分析
print("=== 欠損分析 ===")
missing_report = pd.DataFrame({
'count': df.isnull().sum(),
'pct': (df.isnull().sum() / len(df) * 100).round(1)
})
print(missing_report[missing_report['count'] > 0])
# 3. 重要フィールドが欠損の行を削除
df = df.dropna(subset=['customer_id', 'name'])
# 4. 数値列を補完
df['age'] = df['age'].fillna(df['age'].median())
df['purchase_amount'] = df['purchase_amount'].interpolate(method='linear')
# 5. カテゴリ列を補完
df['category'] = df['category'].fillna(df['category'].mode()[0])
# 6. 検証:欠損がゼロであることを確認
print(f"\n=== クリーニング後 ===")
print(f"残存欠損数: {df.isnull().sum().sum()}")
print(df.head(10))
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンにより若干異なる場合があります。
❓ よくある質問
Q NaNとNoneの違いは何ですか?
A NaN(np.nan)はIEEE 754の浮動小数点欠損値であり、NaN ≠ NaNです。NoneはPythonのオブジェクトレベルの欠損値であり、None == Noneです。Pandasでは、どちらもisnull()で検出されます。整数列にNaNが含まれると自動的にfloat64に変換されますが、nullableなInt64型(大文字のI)を使用すれば整数型を維持でき、欠損値はpd.NAで表現されます。
Q dropnaとfillnaのどちらを選ぶべきですか?
A 欠損が5%未満で重要でない場合 → dropnaで削除。5〜30%の欠損で妥当なデフォルト値がある場合 → fillnaで補完(平均/中央値/最頻値)。時系列データの場合 → interpolateで補間。50%超の欠損の場合 → 列の削除を検討。基本原則:削除は情報を失い、補完はバイアスをもたらし、補間はトレンドを仮定します。
Q fillna(method='ffill')でエラーが出るのはなぜですか?
A Pandas 2.xではfillnaのmethodパラメータが非推奨になりました。代わりにdf.ffill()(前方補完)とdf.bfill()(後方補完)を使用してください。ffillは時系列データ(直前の有効値を使用)には適していますが、ランダムな順序のデータには不適切です(誤った値が遠くまで伝播する可能性があります)。
Q どの補間メソッドが最適ですか?
A デフォルトのlinearが最も汎用的です。時系列データにはmethod='time'(時間距離で重み付け)を使用します。明確なトレンドにはquadratic/cubicを使用します。ステップ変化データにはnearestを使用します。splineは平滑化に適していますが、orderパラメータの調整が必要です。まずはlinearから始めて、結果が不十分な場合のみ切り替えてください。
Q fillna(inplace=True)はまだ安全ですか?
A Pandas 3.xのCopy-on-Writeモードでは、inplaceはまだ安全です。元のデータフレームを直接変更します。ただし、より推奨されるスタイルは
df = df.fillna(...)のように変数に代入で戻す方法で、意味的に明確です。CoWはどちらのアプローチでも同じ結果を保証します。Q 列にNaNが含まれているか確認するには?
A 最も簡単な方法:
df['col'].isnull().any()でTrue/Falseを返します。カウントするには:df['col'].isnull().sum()で欠損数を返します。割合は:df['col'].isnull().mean() * 100。テーブル全体を確認するには:df.isnull().any().any()でデータフレームにNaNが1つでもあるか判定できます。Q NaNがあると整数列がfloatになるのはなぜですか?
A NumPyのint64はNaNを表現できません(すべてのビットが有効な桁です)。Pandasはnp.nanを格納するために列全体をfloat64に変換するしかありません。解決策:nullable型
df['col'].astype('Int64')(大文字のI)を使用すれば、整数のセマンティクスを維持しながらpd.NAで欠損値を表現できます。📖 まとめ
- NaNは自分自身と等しくない(IEEE 754)ため、isnull()で検出する必要があり、==は使用できません
- isnull / notnullで欠損値を検出し、.sum()でカウント、.any()で簡易チェックが可能です
- dropnaで欠損行・欠損列を削除:how='any'/'all'、threshしきい値、subsetで列指定チェック
- fillnaで値を補完:定数 / 平均 / 中央値 / 最頻値。Pandas 2.xではmethodの代わりにffill()/bfill()を使用
- interpolateで補間:linearがデフォルト、timeは時間加重、quadratic/cubicは曲線フィット
- Copy-on-Write環境での安全な操作:変数への代入またはinplaceを使用
- ワークフロー:検出・分析 → 修復不能なものを削除 → 補完/補間 → 欠損ゼロを検証
📝 練習問題
- 基礎(難易度 ⭐):NaNを含むデータフレームを作成してください。isnull().sum()で各列の欠損数をカウントし、dropna()で欠損行を削除して、行数の変化を比較してください。
- 応用(難易度 ⭐⭐):時系列データフレーム(10行、うちNaNが3つ)を作成してください。それぞれffill() / bfill() / interpolate(method='linear')で補完し、3つの結果の違いを比較してください。
- 発展(難易度 ⭐⭐⭐):50行の顧客データ(age/salary/categoryがそれぞれ15%欠損)をシミュレートし、完全なクリーニングワークフローを完成させてください:欠損分析 → dropna(subset=[重要列]) → ageを平均で補完 → salaryを補間 → categoryを最頻値で補完 → 欠損ゼロを検証。



