Step 27:欠損値を処理しよう

🔍 ステップ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

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE