📚 背景知識(読んでから問題へ)
標準偏差とは?
データがどれだけ「バラついている」かを表す数値です。
- 標準偏差が小さい → データが平均のそばに集まっている(整列した社員の身長)
- 標準偏差が大きい → データが広く散らばっている(日本全国の年収)
外れ値(Outlier)とは?
他のデータとかけ離れた値のこと。
例: クラスの身長 [160, 162, 165, 168, 170, 210] → 210 は外れ値
IQR(四分位範囲)法
外れ値を検出するための定番手法。
Q1 = 25パーセンタイル(下から25%の位置の値)
Q3 = 75パーセンタイル(下から75%の位置の値)
IQR = Q3 - Q1
外れ値の境界:
下限 = Q1 - 1.5 × IQR
上限 = Q3 + 1.5 × IQR
この範囲を外れた値を「外れ値」として扱います。箱ひげ図(boxplot)はこの方法を視覚化したものです。
Kaggleでなぜ重要?
- 外れ値をそのまま学習に使うとモデルが歪む(特に線形回帰)
- 外れ値をどう扱うか(除去・変換・保持)が特徴量エンジニアリングの基本
📝 問題
以下のデータは「架空のECサイトの顧客1人あたりの月間購入金額(円)」です。
import numpy as np
import pandas as pd
data = [1200, 1500, 1800, 1600, 1400, 1700, 1300, 1550, 1650, 1450,
1800, 1200, 1350, 1600, 1500, 99800, 1400, 1700, 1250, 1600]
以下の分析を行いなさい:
- 平均・中央値・標準偏差を計算する
- IQR法で外れ値の境界(上限・下限)を計算し、外れ値を特定する
- 外れ値を除いた平均と除く前の平均を比較し、外れ値の影響を説明する
- 箱ひげ図(boxplot)を描いて外れ値を視覚的に確認する
🔍 ヒント(段階的開示)
ヒント1(方向性)
numpy と pandas の基本統計関数(mean, median, std, percentile)を使えばほとんど解ける。
boxplot は matplotlib の plt.boxplot() または seaborn の sns.boxplot() を使う。
ヒント2(IQR法のコード)
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
iqr = q3 - q1
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr
ヒント3(外れ値の除去)
data_array = np.array(data)
mask = (data_array >= lower) & (data_array <= upper)
data_clean = data_array[mask]
outliers = data_array[~mask]
✅ 模範解答
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
data = [1200, 1500, 1800, 1600, 1400, 1700, 1300, 1550, 1650, 1450,
1800, 1200, 1350, 1600, 1500, 99800, 1400, 1700, 1250, 1600]
data_array = np.array(data)
# 1. 基本統計
mean_val = np.mean(data_array)
median_val = np.median(data_array)
std_val = np.std(data_array)
print(f"平均: {mean_val:.1f} 円")
print(f"中央値: {median_val:.1f} 円")
print(f"標準偏差: {std_val:.1f} 円")
# 2. IQR法で外れ値を検出
q1 = np.percentile(data_array, 25)
q3 = np.percentile(data_array, 75)
iqr = q3 - q1
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr
print(f"\nQ1: {q1}, Q3: {q3}, IQR: {iqr}")
print(f"外れ値の境界: [{lower:.1f}, {upper:.1f}]")
mask = (data_array >= lower) & (data_array <= upper)
outliers = data_array[~mask]
data_clean = data_array[mask]
print(f"外れ値: {outliers}")
# 3. 外れ値除去前後の比較
mean_before = np.mean(data_array)
mean_after = np.mean(data_clean)
print(f"\n外れ値除去前の平均: {mean_before:.1f} 円")
print(f"外れ値除去後の平均: {mean_after:.1f} 円")
print(f"差: {mean_before - mean_after:.1f} 円")
# 4. 箱ひげ図
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].boxplot(data_array)
axes[0].set_title("外れ値あり")
axes[0].set_ylabel("購入金額(円)")
axes[1].boxplot(data_clean)
axes[1].set_title("外れ値除去後")
axes[1].set_ylabel("購入金額(円)")
plt.tight_layout()
plt.savefig("boxplot_outlier.png")
plt.show()
print("boxplot_outlier.png を保存しました")
実行結果:
▶ 出力を見る
平均: 6665.0 円
中央値: 1525.0 円
標準偏差: 21668.9 円
Q1: 1375.0, Q3: 1675.0, IQR: 300.0
外れ値の境界: [925.0, 2125.0]
外れ値: [99800]
外れ値除去前の平均: 6665.0 円
外れ値除去後の平均: 1497.4 円
差: 5167.6 円🪜 Step-by-Step 解説
1
基本統計の確認
平均が 6665円 なのに中央値が 1525円。この差が大きいのは 外れ値(99800円)が平均を引き上げているから。
→ 外れ値がある場合、平均より 中央値のほうが実態を表す。
2
IQR法の計算
- Q1(25パーセンタイル)= 1375円:下位25%の顧客の「境界値」
- Q3(75パーセンタイル)= 1675円:上位25%の顧客の「境界値」
- IQR = 300円:中央50%のデータの幅
- 上限 = 1675 + 1.5 × 300 = 2125円
- 99800円 >> 2125円 → 明確な外れ値
3
外れ値の影響を数字で確認
平均が 6665円 → 1497円 に大幅変化(差: 5167円)。
たった1件の外れ値が平均を4倍以上に膨らませていた。
4
箱ひげ図の読み方
|---------|-------| ← ひげ(whisker)= 境界まで
[ ======== ] ← 箱 = Q1〜Q3(中央50%)
| ← 中央線 = 中央値
× ← 点 = 外れ値(boxの外の点)
📐 数学・統計の補足(文系向け)
標準偏差 21668円 は非常に大きい値。これは「ほとんどの顧客は1500円前後なのに、1人だけ10万円近く使っている」という状況を数字で示している。
標準偏差が大きい = データが信用できないかもしれない。Kaggleでは最初にこのバラつきをチェックするのがルーティン。
🏆 Kaggleでの実践的な使い方
- EDA(探索的データ分析)の第一歩:
df.describe()で std を確認 → stdが mean と同じくらい大きければ外れ値を疑う - ターゲット変数の外れ値: House Prices コンペでは価格の外れ値を log変換して扱う(後のPhaseで学ぶ)
- 特徴量エンジニアリング: 外れ値をclippingする
np.clip(data, lower, upper)もよく使う手法
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
| 外れ値を必ず除去する | 「ノイズだ」と思い込む | 外れ値が重要な情報の場合も。まず「なぜ外れているか」を考える |
| 平均だけで判断する | 最も馴染みのある統計量 | 中央値・stdと一緒に確認するのが基本 |
| IQRの1.5は絶対 | 教科書通り | 2.0や3.0を使うこともある。データと問題次第 |
🚀 次のステップ
- 発展: log変換(外れ値を小さく見せる変換)
- 次回予告: 確率の基礎(確率とは何か)