🔍 ステップ27: 欠損値を処理しよう
データの「穴」を見つけて、適切に処理する方法を学ぼう!
ステップ26では、データの並び替えと集計を学びました。今回は、実際のデータ分析で必ず遭遇する欠損値(けっそんち)の処理を学びます。欠損値とは「データが入っていない部分」のことで、適切に処理しないと分析結果がおかしくなることがあります。
📖 このステップで学ぶこと
・欠損値とは何か
・isnull()で欠損値を確認する
・dropna()で欠損値を削除する
・fillna()で欠損値を補完する
・実データでの処理方法
学習時間の目安: 2.5〜3時間
🎯 1. 欠損値とは?
欠損値(けっそんち)とは、データが入っていない部分のことです。PandasではNaN(Not a Number = 数字じゃない)と表示されます。
🔰 欠損値が発生する理由
💡 なぜ欠損値が発生するのか?
・アンケートで回答されなかった項目
・センサーの故障でデータが記録されなかった
・データ入力時の入力漏れ
・該当するデータがない(例:独身者の「配偶者名」)
・異なるシステム間でのデータ統合時のミスマッチ
📝 欠損値の例を見てみよう
コード:欠損値を含むデータを作成
# Pandasとnumpyをインポート
import pandas as pd
import numpy as np
# 欠損値を含むデータを作成
# np.nanは「欠損値」を表す特別な値
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'身長': [170, np.nan, 165, 158, 180],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
print("欠損値を含むデータ:")
print(df)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
欠損値を含むデータ: 名前 年齢 身長 体重 0 太郎 25.0 170.0 65.0 1 花子 30.0 NaN 55.0 2 次郎 NaN 165.0 NaN 3 美咲 28.0 158.0 NaN 4 健太 35.0 180.0 75.0
💡 コードの解説
import numpy as np
・NumPyライブラリをnpという短い名前でインポート
・欠損値を作るために使います
np.nan
・「欠損値」を表す特別な値
・NaN = Not a Number(数字ではない)の略
表示される「NaN」
・データが入っていない場所を示します
・花子の身長、次郎の年齢と体重、美咲の体重が欠損しています
📌 欠損値の表記いろいろ
| 表記 | 説明 | 使う場面 |
| NaN | Pandasでの欠損値の表示 | DataFrameに表示される |
| np.nan | NumPyの欠損値 | 欠損値を作る時に使う |
| None | Pythonの「値がない」を表す | 文字列データの欠損など |
🔎 2. 欠損値を確認する
欠損値を処理する前に、まず「どこに」「いくつ」欠損値があるかを確認する必要があります。
🔰 isnull()で欠損値を見つける
isnull()は、欠損値がある場所にTrue、それ以外にFalseを返します。
📝 欠損値確認の書き方
df.isnull() → 欠損値がある場所にTrue
df.isnull().sum() → 各列の欠損値の個数
df.notnull() → 欠損値がない場所にTrue
コード:欠損値の場所を確認
import pandas as pd
import numpy as np
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'身長': [170, np.nan, 165, 158, 180],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
print("元のデータ:")
print(df)
print()
# 欠損値の場所を確認(TrueとFalseで表示)
print("欠損値の場所(True=欠損):")
print(df.isnull())
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ:
名前 年齢 身長 体重
0 太郎 25.0 170.0 65.0
1 花子 30.0 NaN 55.0
2 次郎 NaN 165.0 NaN
3 美咲 28.0 158.0 NaN
4 健太 35.0 180.0 75.0
欠損値の場所(True=欠損):
名前 年齢 身長 体重
0 False False False False
1 False False True False
2 False True False True
3 False False False True
4 False False False False
💡 結果の読み方
True:そこに欠損値がある
False:そこにはデータがある
例:
・1行目(花子)の身長がTrue → 身長が欠損
・2行目(次郎)の年齢と体重がTrue → 両方欠損
📝 列ごとの欠損値の個数を数える
コード:各列の欠損値の個数
import pandas as pd
import numpy as np
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'身長': [170, np.nan, 165, 158, 180],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
# 各列の欠損値の個数
print("各列の欠損値の個数:")
print(df.isnull().sum())
print()
# 欠損値の総数
print("欠損値の総数:")
print(df.isnull().sum().sum())
print()
# 欠損値の割合(%)
print("欠損値の割合(%):")
missing_rate = df.isnull().sum() / len(df) * 100
print(missing_rate)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
各列の欠損値の個数: 名前 0 年齢 1 身長 1 体重 3 dtype: int64 欠損値の総数: 5 欠損値の割合(%): 名前 0.0 年齢 20.0 身長 20.0 体重 60.0 dtype: float64
💡 コードの解説
df.isnull().sum()
・isnull()でTrue/Falseを取得
・sum()でTrueの数を数える(Trueは1、Falseは0として計算)
df.isnull().sum().sum()
・列ごとの欠損数をさらに合計
・全体で何個の欠損があるかがわかる
df.isnull().sum() / len(df) * 100
・欠損数を全体の行数で割ってパーセントに変換
・体重は60%が欠損している(5行中3行)
📘 行ごとの欠損値の個数
コード:各行の欠損値の個数
import pandas as pd
import numpy as np
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'身長': [170, np.nan, 165, 158, 180],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
# 各行の欠損値の個数(axis=1で行方向)
print("各行の欠損値の個数:")
print(df.isnull().sum(axis=1))
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
各行の欠損値の個数: 0 0 1 1 2 2 3 1 4 0 dtype: int64
💡 axis(アクシス)の意味
axis=0(デフォルト):列方向(縦)に計算
axis=1:行方向(横)に計算
結果を見ると:
・0行目(太郎):欠損なし
・1行目(花子):1つ欠損(身長)
・2行目(次郎):2つ欠損(年齢と体重)
・3行目(美咲):1つ欠損(体重)
・4行目(健太):欠損なし
🗑️ 3. 欠損値を削除する(dropna)
dropna()を使うと、欠損値がある行や列を削除できます。データが十分にある場合や、欠損が少ない場合に有効です。
🔰 欠損値がある行を削除
📝 dropna()の書き方
df.dropna() → 1つでも欠損値がある行を削除
df.dropna(how=’all’) → 全てが欠損値の行のみ削除
df.dropna(subset=[‘列名’]) → 特定の列に欠損がある行を削除
df.dropna(axis=1) → 欠損値がある列を削除
コード:欠損値がある行を削除
import pandas as pd
import numpy as np
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'身長': [170, np.nan, 165, 158, 180],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
print("元のデータ:")
print(df)
print()
# 欠損値がある行を削除
df_dropped = df.dropna()
print("欠損値を削除した結果:")
print(df_dropped)
print(f"\n元のデータ: {len(df)}行 → 削除後: {len(df_dropped)}行")
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ: 名前 年齢 身長 体重 0 太郎 25.0 170.0 65.0 1 花子 30.0 NaN 55.0 2 次郎 NaN 165.0 NaN 3 美咲 28.0 158.0 NaN 4 健太 35.0 180.0 75.0 欠損値を削除した結果: 名前 年齢 身長 体重 0 太郎 25.0 170.0 65.0 4 健太 35.0 180.0 75.0 元のデータ: 5行 → 削除後: 2行
⚠️ 注意!
欠損値を削除しすぎると、データが減りすぎて分析できなくなることがあります。
この例では、5行あったデータが2行になってしまいました!
欠損が多いデータでは、削除ではなく補完を検討しましょう。
📝 特定の列に欠損値がある行だけ削除
全ての欠損を削除するのではなく、「この列だけは欠損があってはダメ」という場合に使います。
コード:特定の列の欠損値を削除
import pandas as pd
import numpy as np
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'身長': [170, np.nan, 165, 158, 180],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
print("元のデータ:")
print(df)
print()
# 「年齢」に欠損値がある行だけ削除
df_age_dropped = df.dropna(subset=['年齢'])
print("年齢が欠損している行を削除:")
print(df_age_dropped)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ: 名前 年齢 身長 体重 0 太郎 25.0 170.0 65.0 1 花子 30.0 NaN 55.0 2 次郎 NaN 165.0 NaN 3 美咲 28.0 158.0 NaN 4 健太 35.0 180.0 75.0 年齢が欠損している行を削除: 名前 年齢 身長 体重 0 太郎 25.0 170.0 65.0 1 花子 30.0 NaN 55.0 3 美咲 28.0 158.0 NaN 4 健太 35.0 180.0 75.0
💡 subset=[‘年齢’]の効果
「年齢」列だけをチェックして、欠損がある行を削除します。
結果を見ると:
・次郎(年齢がNaN)だけが削除された
・花子と美咲は身長や体重にNaNがあるが、年齢はあるので残っている
📌 dropna()のオプションまとめ
| オプション | 説明 | 使用例 |
| how=’any’ | 1つでも欠損があれば削除(デフォルト) | df.dropna() |
| how=’all’ | 全てが欠損の場合のみ削除 | df.dropna(how=’all’) |
| subset | 特定の列だけチェック | df.dropna(subset=[‘年齢’]) |
| axis=1 | 列を削除 | df.dropna(axis=1) |
🔧 4. 欠損値を補完する(fillna)
fillna()を使うと、欠損値を指定した値で埋めることができます。データを削除したくない場合に使います。
🔰 特定の値で埋める
📝 fillna()の書き方
df.fillna(値) → 全ての欠損値を指定した値で埋める
df[‘列名’].fillna(値) → 特定の列の欠損値を埋める
df.fillna(df.mean()) → 各列の平均値で埋める
コード:欠損値を0で埋める
import pandas as pd
import numpy as np
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'身長': [170, np.nan, 165, 158, 180],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
print("元のデータ:")
print(df)
print()
# 欠損値を0で埋める
df_filled = df.fillna(0)
print("欠損値を0で埋めた結果:")
print(df_filled)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ: 名前 年齢 身長 体重 0 太郎 25.0 170.0 65.0 1 花子 30.0 NaN 55.0 2 次郎 NaN 165.0 NaN 3 美咲 28.0 158.0 NaN 4 健太 35.0 180.0 75.0 欠損値を0で埋めた結果: 名前 年齢 身長 体重 0 太郎 25.0 170.0 65.0 1 花子 30.0 0.0 55.0 2 次郎 0.0 165.0 0.0 3 美咲 28.0 158.0 0.0 4 健太 35.0 180.0 75.0
⚠️ 0で埋めるのは適切?
年齢0歳、身長0cm、体重0kgは現実的ではありません!
0で埋めるのが適切な場合:
・カウント系のデータ(訪問回数、購入回数など)
・「なければ0」と考えられるデータ
年齢や身長などは、平均値や中央値で埋める方が適切です。
📝 平均値で埋める
数値データの欠損値は、平均値で埋めることが多いです。
コード:平均値で欠損値を埋める
import pandas as pd
import numpy as np
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'身長': [170, np.nan, 165, 158, 180],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
# まず平均値を確認
print("各列の平均値:")
print(f"年齢の平均: {df['年齢'].mean():.1f}")
print(f"身長の平均: {df['身長'].mean():.1f}")
print(f"体重の平均: {df['体重'].mean():.1f}")
print()
# 各列の平均値で欠損値を埋める
df_mean_filled = df.copy()
df_mean_filled['年齢'] = df_mean_filled['年齢'].fillna(df['年齢'].mean())
df_mean_filled['身長'] = df_mean_filled['身長'].fillna(df['身長'].mean())
df_mean_filled['体重'] = df_mean_filled['体重'].fillna(df['体重'].mean())
print("平均値で埋めた結果:")
print(df_mean_filled)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
各列の平均値: 年齢の平均: 29.5 身長の平均: 168.2 体重の平均: 65.0 平均値で埋めた結果: 名前 年齢 身長 体重 0 太郎 25.00 170.00 65.00 1 花子 30.00 168.25 55.00 2 次郎 29.50 165.00 65.00 3 美咲 28.00 158.00 65.00 4 健太 35.00 180.00 75.00
💡 コードの解説
df.copy()
・元のDataFrameのコピーを作成
・元データを変更しないようにするため
df[‘年齢’].mean()
・年齢列の平均値を計算
・NaNは自動的に無視されます
.fillna(df[‘年齢’].mean())
・欠損値を平均値で埋める
📘 中央値で埋める
外れ値(極端に大きいor小さい値)がある場合は、平均値より中央値の方が適切です。
コード:中央値で欠損値を埋める
import pandas as pd
import numpy as np
data = {
'名前': ['太郎', '花子', '次郎', '美咲', '健太'],
'年齢': [25, 30, np.nan, 28, 35],
'体重': [65, 55, np.nan, np.nan, 75]
}
df = pd.DataFrame(data)
print("元のデータ:")
print(df)
print()
# 中央値を確認
print(f"年齢の中央値: {df['年齢'].median()}")
print(f"体重の中央値: {df['体重'].median()}")
print()
# 中央値で埋める
df_median = df.copy()
df_median['年齢'] = df_median['年齢'].fillna(df['年齢'].median())
df_median['体重'] = df_median['体重'].fillna(df['体重'].median())
print("中央値で埋めた結果:")
print(df_median)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ: 名前 年齢 体重 0 太郎 25.0 65.0 1 花子 30.0 55.0 2 次郎 NaN NaN 3 美咲 28.0 NaN 4 健太 35.0 75.0 年齢の中央値: 29.0 体重の中央値: 65.0 中央値で埋めた結果: 名前 年齢 体重 0 太郎 25.0 65.0 1 花子 30.0 55.0 2 次郎 29.0 65.0 3 美咲 28.0 65.0 4 健太 35.0 75.0
🔄 前後の値で埋める(時系列データ向け)
日付や時刻で並んでいるデータでは、前の値や後ろの値で埋めることが適切な場合があります。
コード:前の値・後ろの値で埋める
import pandas as pd
import numpy as np
# 時系列データの例(毎日の気温)
time_data = {
'日付': ['1/1', '1/2', '1/3', '1/4', '1/5'],
'気温': [15.2, np.nan, np.nan, 18.5, 19.1]
}
df = pd.DataFrame(time_data)
print("元のデータ:")
print(df)
print()
# 前の値で埋める(forward fill)
df_ffill = df.copy()
df_ffill['気温'] = df_ffill['気温'].ffill()
print("前の値で埋める(ffill):")
print(df_ffill)
print()
# 後ろの値で埋める(backward fill)
df_bfill = df.copy()
df_bfill['気温'] = df_bfill['気温'].bfill()
print("後ろの値で埋める(bfill):")
print(df_bfill)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ: 日付 気温 0 1/1 15.2 1 1/2 NaN 2 1/3 NaN 3 1/4 18.5 4 1/5 19.1 前の値で埋める(ffill): 日付 気温 0 1/1 15.2 1 1/2 15.2 2 1/3 15.2 3 1/4 18.5 4 1/5 19.1 後ろの値で埋める(bfill): 日付 気温 0 1/1 15.2 1 1/2 18.5 2 1/3 18.5 3 1/4 18.5 4 1/5 19.1
💡 ffillとbfillの意味
ffill(forward fill)
・前(上)の値で欠損を埋める
・1/2と1/3は、直前の1/1の値(15.2)で埋まる
bfill(backward fill)
・後ろ(下)の値で欠損を埋める
・1/2と1/3は、直後の1/4の値(18.5)で埋まる
時系列データでは、直前の値が続くと仮定してffillを使うことが多いです。
📌 補完方法の選び方
| 補完方法 | 適した場面 | 例 |
| 0で埋める | カウント系のデータ | 訪問回数、購入回数 |
| 平均値 | 数値データの一般的な補完 | テストの点数、売上 |
| 中央値 | 外れ値がある場合 | 年収(極端に高い人がいる場合) |
| 前後の値 | 時系列データ | 毎日の気温、株価 |
| 削除 | 欠損が少なく、データが十分ある場合 | 全体の5%未満の欠損 |
📊 5. 実データでの処理例
実際のデータ分析では、複数の手法を組み合わせて欠損値を処理します。
📋 例:アンケートデータの処理
コード:アンケートデータの欠損値処理
import pandas as pd
import numpy as np
# アンケートデータを作成
survey_data = {
'ID': [1, 2, 3, 4, 5, 6, 7, 8],
'名前': ['Aさん', 'Bさん', 'Cさん', 'Dさん', 'Eさん', 'Fさん', 'Gさん', 'Hさん'],
'満足度': [5, 4, np.nan, 5, 3, np.nan, 4, 5],
'価格評価': [4, 5, 3, np.nan, 4, 3, np.nan, 5],
'コメント': ['良い', np.nan, '普通', '良い', np.nan, '悪い', '良い', np.nan]
}
df = pd.DataFrame(survey_data)
print("元のアンケートデータ:")
print(df)
print()
# 欠損値の状況を確認
print("欠損値の状況:")
print(df.isnull().sum())
print()
# 処理方針を決める
# 1. 満足度・価格評価(数値):平均値で補完
# 2. コメント(文字列):「未回答」で補完
survey_clean = df.copy()
# 満足度と価格評価を平均値で補完
survey_clean['満足度'] = survey_clean['満足度'].fillna(df['満足度'].mean())
survey_clean['価格評価'] = survey_clean['価格評価'].fillna(df['価格評価'].mean())
# コメントを「未回答」で補完
survey_clean['コメント'] = survey_clean['コメント'].fillna('未回答')
print("処理後のデータ:")
print(survey_clean)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のアンケートデータ: ID 名前 満足度 価格評価 コメント 0 1 Aさん 5.0 4.0 良い 1 2 Bさん 4.0 5.0 NaN 2 3 Cさん NaN 3.0 普通 3 4 Dさん 5.0 NaN 良い 4 5 Eさん 3.0 4.0 NaN 5 6 Fさん NaN 3.0 悪い 6 7 Gさん 4.0 NaN 良い 7 8 Hさん 5.0 5.0 NaN 欠損値の状況: ID 0 名前 0 満足度 2 価格評価 2 コメント 3 dtype: int64 処理後のデータ: ID 名前 満足度 価格評価 コメント 0 1 Aさん 5.000000 4.000000 良い 1 2 Bさん 4.000000 5.000000 未回答 2 3 Cさん 4.333333 3.000000 普通 3 4 Dさん 5.000000 4.000000 良い 4 5 Eさん 3.000000 4.000000 未回答 5 6 Fさん 4.333333 3.000000 悪い 6 7 Gさん 4.000000 4.000000 良い 7 8 Hさん 5.000000 5.000000 未回答
💡 処理のポイント
数値データ(満足度、価格評価)
・平均値で補完
・満足度の平均は約4.33なので、未回答者も「まあまあ満足」と仮定
文字列データ(コメント)
・「未回答」という文字列で補完
・平均値は計算できないので、意味のある文字列を入れる
📝 練習問題
ここまで学んだことを、実際に手を動かして確認しましょう。
問題1:欠損値の確認(初級)
📋 問題
以下のデータの欠損値を確認し、各列の欠損値の個数を表示してください。
・商品: [‘A’, ‘B’, ‘C’, ‘D’, ‘E’]
・価格: [100, 200, np.nan, 150, 180]
・在庫: [50, np.nan, 30, np.nan, 40]
解答例を見る
コード
import pandas as pd
import numpy as np
data = {
'商品': ['A', 'B', 'C', 'D', 'E'],
'価格': [100, 200, np.nan, 150, 180],
'在庫': [50, np.nan, 30, np.nan, 40]
}
df = pd.DataFrame(data)
print("データ:")
print(df)
print("\n欠損値の個数:")
print(df.isnull().sum())
print("\n欠損値の割合(%):")
print(df.isnull().sum() / len(df) * 100)
実行結果
データ: 商品 価格 在庫 0 A 100.0 50.0 1 B 200.0 NaN 2 C NaN 30.0 3 D 150.0 NaN 4 E 180.0 40.0 欠損値の個数: 商品 0 価格 1 在庫 2 dtype: int64 欠損値の割合(%): 商品 0.0 価格 20.0 在庫 40.0 dtype: float64
問題2:欠損値の削除(初級)
📋 問題
問題1のデータから、欠損値がある行を削除してください。
解答例を見る
コード
import pandas as pd
import numpy as np
data = {
'商品': ['A', 'B', 'C', 'D', 'E'],
'価格': [100, 200, np.nan, 150, 180],
'在庫': [50, np.nan, 30, np.nan, 40]
}
df = pd.DataFrame(data)
# 欠損値がある行を削除
df_clean = df.dropna()
print("欠損値を削除した結果:")
print(df_clean)
print(f"\n元のデータ: {len(df)}行")
print(f"削除後: {len(df_clean)}行")
print(f"削除された行: {len(df) - len(df_clean)}行")
実行結果
欠損値を削除した結果: 商品 価格 在庫 0 A 100.0 50.0 4 E 180.0 40.0 元のデータ: 5行 削除後: 2行 削除された行: 3行
問題3:平均値で補完(中級)
📋 問題
問題1のデータの「価格」と「在庫」の欠損値を、それぞれの列の平均値で補完してください。
解答例を見る
コード
import pandas as pd
import numpy as np
data = {
'商品': ['A', 'B', 'C', 'D', 'E'],
'価格': [100, 200, np.nan, 150, 180],
'在庫': [50, np.nan, 30, np.nan, 40]
}
df = pd.DataFrame(data)
# 平均値を計算
price_mean = df['価格'].mean()
stock_mean = df['在庫'].mean()
print(f"価格の平均: {price_mean}")
print(f"在庫の平均: {stock_mean}")
# 平均値で補完
df_filled = df.copy()
df_filled['価格'] = df_filled['価格'].fillna(price_mean)
df_filled['在庫'] = df_filled['在庫'].fillna(stock_mean)
print("\n補完後のデータ:")
print(df_filled)
実行結果
価格の平均: 157.5 在庫の平均: 40.0 補完後のデータ: 商品 価格 在庫 0 A 100.0 50.0 1 B 200.0 40.0 2 C 157.5 30.0 3 D 150.0 40.0 4 E 180.0 40.0
問題4:特定の列の欠損値を削除(中級)
📋 問題
以下のデータから、「価格」に欠損値がある行だけを削除してください。「在庫」の欠損は残してOKです。
・商品: [‘A’, ‘B’, ‘C’, ‘D’, ‘E’]
・価格: [100, np.nan, 200, np.nan, 150]
・在庫: [50, 30, np.nan, 40, np.nan]
解答例を見る
コード
import pandas as pd
import numpy as np
data = {
'商品': ['A', 'B', 'C', 'D', 'E'],
'価格': [100, np.nan, 200, np.nan, 150],
'在庫': [50, 30, np.nan, 40, np.nan]
}
df = pd.DataFrame(data)
print("元のデータ:")
print(df)
# 「価格」に欠損値がある行だけ削除
df_clean = df.dropna(subset=['価格'])
print("\n価格の欠損を削除:")
print(df_clean)
実行結果
元のデータ: 商品 価格 在庫 0 A 100.0 50.0 1 B NaN 30.0 2 C 200.0 NaN 3 D NaN 40.0 4 E 150.0 NaN 価格の欠損を削除: 商品 価格 在庫 0 A 100.0 50.0 2 C 200.0 NaN 4 E 150.0 NaN
問題5:時系列データの補完(上級)
📋 問題
以下の温度データの欠損値を、前の値で埋めてください(forward fill)。
・時刻: [‘9:00′, ’10:00′, ’11:00′, ’12:00′, ’13:00′, ’14:00’]
・温度: [18.5, np.nan, np.nan, 22.3, np.nan, 24.1]
解答例を見る
コード
import pandas as pd
import numpy as np
data = {
'時刻': ['9:00', '10:00', '11:00', '12:00', '13:00', '14:00'],
'温度': [18.5, np.nan, np.nan, 22.3, np.nan, 24.1]
}
df = pd.DataFrame(data)
print("元のデータ:")
print(df)
# 前の値で埋める(forward fill)
df_filled = df.copy()
df_filled['温度'] = df_filled['温度'].ffill()
print("\n前の値で埋めた結果:")
print(df_filled)
実行結果
元のデータ:
時刻 温度
0 9:00 18.5
1 10:00 NaN
2 11:00 NaN
3 12:00 22.3
4 13:00 NaN
5 14:00 24.1
前の値で埋めた結果:
時刻 温度
0 9:00 18.5
1 10:00 18.5
2 11:00 18.5
3 12:00 22.3
4 13:00 22.3
5 14:00 24.1
問題6:総合演習(上級)
📋 問題
以下のデータについて、欠損値の状況をレポートし、適切な方法で補完してください。
・名前: [‘Aさん’, ‘Bさん’, ‘Cさん’, ‘Dさん’, ‘Eさん’]
・年齢: [25, 30, np.nan, 28, 35]
・評価: [‘良い’, np.nan, ‘普通’, ‘良い’, np.nan]
解答例を見る
コード
import pandas as pd
import numpy as np
data = {
'名前': ['Aさん', 'Bさん', 'Cさん', 'Dさん', 'Eさん'],
'年齢': [25, 30, np.nan, 28, 35],
'評価': ['良い', np.nan, '普通', '良い', np.nan]
}
df = pd.DataFrame(data)
print("="*50)
print("欠損値分析レポート")
print("="*50)
print("\n元のデータ:")
print(df)
print("\n【欠損値の状況】")
print(f"年齢: {df['年齢'].isnull().sum()}件 ({df['年齢'].isnull().sum()/len(df)*100:.0f}%)")
print(f"評価: {df['評価'].isnull().sum()}件 ({df['評価'].isnull().sum()/len(df)*100:.0f}%)")
# 補完処理
df_clean = df.copy()
# 年齢は平均値で補完
df_clean['年齢'] = df_clean['年齢'].fillna(df['年齢'].mean())
# 評価は「未回答」で補完
df_clean['評価'] = df_clean['評価'].fillna('未回答')
print("\n【補完後のデータ】")
print(df_clean)
print("\n【補完方法】")
print(f"・年齢: 平均値({df['年齢'].mean():.1f}歳)で補完")
print("・評価:「未回答」で補完")
実行結果
==================================================
欠損値分析レポート
==================================================
元のデータ:
名前 年齢 評価
0 Aさん 25.0 良い
1 Bさん 30.0 NaN
2 Cさん NaN 普通
3 Dさん 28.0 良い
4 Eさん 35.0 NaN
【欠損値の状況】
年齢: 1件 (20%)
評価: 2件 (40%)
【補完後のデータ】
名前 年齢 評価
0 Aさん 25.0 良い
1 Bさん 30.0 未回答
2 Cさん 29.5 普通
3 Dさん 28.0 良い
4 Eさん 35.0 未回答
【補完方法】
・年齢: 平均値(29.5歳)で補完
・評価:「未回答」で補完
🎯 このステップのまとめ
✅ 学んだこと
✓ 欠損値(NaN)はデータが入っていない部分
✓ isnull()で欠損値を確認できる
✓ isnull().sum()で欠損値の個数を数えられる
✓ dropna()で欠損値を削除できる
✓ fillna()で欠損値を補完できる
✓ 平均値、中央値、前後の値など、データに合った補完方法を選べる
✓ 数値データと文字列データで補完方法が異なる
💡 次のステップに進む前に確認
以下のことができるようになったか確認しましょう:
□ isnull()で欠損値を確認できますか?
□ dropna()で欠損値を削除できますか?
□ fillna()で欠損値を補完できますか?
□ 適切な補完方法を選べますか?
これらができたら、次のステップに進みましょう!
❓ よくある質問
Q1: NaNとNoneの違いは何ですか?
A: NaNはNumPyの欠損値で数値データ用、NoneはPythonの「値がない」を表します。Pandasでは両方とも欠損値として扱われますが、内部的にはNaNに統一されます。
Q2: 欠損値を削除すべきか補完すべきか、どう判断しますか?
A: 削除:欠損が少ない(5%未満)、データ量が十分ある場合。補完:欠損が多い、データが貴重な場合。ただし、欠損の理由(なぜ欠損しているか)を考えることが最も重要です。
Q3: 平均値と中央値、どちらで補完すべきですか?
A: 外れ値がない場合:平均値。外れ値がある場合:中央値。例えば年収データなら、極端に高い値に引っ張られないよう中央値が適切です。
Q4: 文字列の欠損値はどう扱えばいいですか?
A: 文字列の場合は、「未回答」「不明」「なし」などの文字列で補完するか、削除します。平均値は使えないので注意しましょう。
Q5: fillna()は元のDataFrameを変更しますか?
A: いいえ、元のDataFrameは変更されません。変更した結果は新しいDataFrameとして返されます。元のDataFrameを変更したい場合は、inplace=Trueオプションを使うか、変数に再代入します。
学習メモ
Pythonデータ分析入門 - Step 27