Day 018 — 分散・標準偏差・外れ値(実践コーディング)

2026-04-27 白 / Phase 1 コーディング 分散・標準偏差・外れ値

📚 背景知識(読んでから問題へ)

復習: 分散と標準偏差

  • 分散: データが平均からどれだけ「ばらついているか」を表す数値。各データの「平均からのズレ²」の平均
  • 標準偏差: 分散の√。元のデータと「同じ単位」になる
  • なぜ²を使うか: ズレにはプラスもマイナスもあるので、そのまま足すと打ち消しあってしまうから

外れ値とは

平均から「極端に離れたデータ」のこと。外れ値があるとモデルの精度が下がったり、統計量が歪んだりする。

IQR(四分位範囲)による外れ値検出

最もよく使われる外れ値検出の方法:

Q1 = 25パーセンタイル(下から25%の位置)
Q3 = 75パーセンタイル(下から75%の位置)
IQR = Q3 - Q1

外れ値 = Q1 - 1.5×IQR より小さい OR Q3 + 1.5×IQR より大きい

📝 問題

以下のコードを完成させ、Titanicデータの Age(年齢)列について:

  1. 平均・中央値・標準偏差 を計算して表示する
  2. IQR法 で外れ値を検出し、外れ値の数と値を表示する
  3. 外れ値を除外 したデータの平均・標準偏差を計算して比較する
  4. 箱ひげ図(boxplot) で外れ値を可視化する
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Titanicデータの読み込み(seabornのサンプルデータを使用)
df = sns.load_dataset('titanic')

# Age列(欠損値を除外)
age = df['age'].dropna()

# ===== ここから実装 =====

# 1. 基本統計量
mean_age    = ___
median_age  = ___
std_age     = ___

print(f"平均: {mean_age:.2f}")
print(f"中央値: {median_age:.2f}")
print(f"標準偏差: {std_age:.2f}")

# 2. IQR法で外れ値を検出
Q1 = ___
Q3 = ___
IQR = ___

lower_bound = ___
upper_bound = ___

outliers = age[___ | ___]
print(f"\n外れ値の数: {len(outliers)}")
print(f"外れ値: {sorted(outliers.values)}")

# 3. 外れ値除外後の統計量
age_clean = age[___ & ___]
print(f"\n【外れ値除外後】")
print(f"平均: {age_clean.mean():.2f}")
print(f"標準偏差: {age_clean.std():.2f}")

# 4. 箱ひげ図
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].___  # 元データの boxplot
axes[0].set_title('Age(元データ)')
axes[1].___  # 外れ値除外後の boxplot
axes[1].set_title('Age(外れ値除外後)')
plt.tight_layout()
plt.show()

🔍 ヒント(段階的開示)

ヒント1(方向性)

pandas Series の .mean() .median() .std() .quantile() を使う。

外れ値のフィルタリングは |(OR)と &(AND)の論理演算子を使う。

ヒント2(アプローチ)
Q1 = age.quantile(0.25)   # 25パーセンタイル
Q3 = age.quantile(0.75)   # 75パーセンタイル
IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 外れ値は lower_bound 未満 OR upper_bound 超
outliers = age[(age < lower_bound) | (age > upper_bound)]
ヒント3(コード骨格)
# 外れ値除外(both conditions must be TRUE)
age_clean = age[(age >= lower_bound) & (age <= upper_bound)]

# boxplot
axes[0].boxplot(age)          # matplotlib の boxplot
axes[1].boxplot(age_clean)

模範解答

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

df = sns.load_dataset('titanic')
age = df['age'].dropna()

# 1. 基本統計量
mean_age   = age.mean()
median_age = age.median()
std_age    = age.std()

print(f"平均: {mean_age:.2f}")
print(f"中央値: {median_age:.2f}")
print(f"標準偏差: {std_age:.2f}")

# 2. IQR法で外れ値を検出
Q1 = age.quantile(0.25)
Q3 = age.quantile(0.75)
IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

outliers = age[(age < lower_bound) | (age > upper_bound)]
print(f"\n外れ値の数: {len(outliers)}")
print(f"外れ値: {sorted(outliers.values)}")

# 3. 外れ値除外後の統計量
age_clean = age[(age >= lower_bound) & (age <= upper_bound)]
print(f"\n【外れ値除外後】")
print(f"平均: {age_clean.mean():.2f}")
print(f"標準偏差: {age_clean.std():.2f}")

# 4. 箱ひげ図
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].boxplot(age)
axes[0].set_title('Age(元データ)')
axes[1].boxplot(age_clean)
axes[1].set_title('Age(外れ値除外後)')
plt.tight_layout()
plt.show()

期待される出力例(実際の数値は近似):

▶ 出力を見る
平均: 29.70
中央値: 28.00
標準偏差: 14.53

外れ値の数: 1
外れ値: [80.0]

【外れ値除外後】
平均: 29.61
標準偏差: 13.89

🪜 Step-by-Step 解説

1
基本統計量の計算
mean_age   = age.mean()    # 全データの合計 ÷ データ数
median_age = age.median()  # データを並べたとき真ん中の値
std_age    = age.std()     # 分散の√(平均からのバラつき)

ポイント: 平均 > 中央値 → 大きな値側に引っ張られている(右裾が重い分布)

2
IQR の計算
Q1 = age.quantile(0.25)  # 下から25%の位置
Q3 = age.quantile(0.75)  # 下から75%の位置
IQR = Q3 - Q1            # 中央50%のデータが収まる範囲

なぜ 1.5×IQR か: 統計学者 John Tukey が経験則として提案した値。正規分布では約99.3%のデータが収まる。

3
外れ値フィルタリングの論理
# 外れ値(除外したいもの): lower 未満 OR upper 超
outliers = age[(age < lower_bound) | (age > upper_bound)]

# 正常値(残したいもの): lower 以上 AND upper 以下
age_clean = age[(age >= lower_bound) & (age <= upper_bound)]

よく混同するポイント: 除外のときは |(OR)、残すときは &(AND)

4
箱ひげ図の読み方
|--[Q1]===中央値====[Q3]--|  ← 箱の部分がIQR
          ↑箱ひげ図の「髭(ひげ)」= 1.5×IQR

箱から外れた点 = 外れ値(●で表示される)

📐 数学・統計の補足(文系向け)

IQR を使う理由(平均・標準偏差じゃダメなの?)

  • 平均・標準偏差は外れ値に影響される → 「外れ値を検出するのに外れ値に影響される指標を使う」は矛盾
  • IQR は「中央50%のデータ」だけを見るので、外れ値に頑健(ロバスト)

Kaggleでよく見る「ロバスト統計」

感度高い(外れ値に弱い)ロバスト(外れ値に強い)
平均中央値
標準偏差IQR
最小・最大値5・95パーセンタイル

🏆 Kaggleでの実践的な使い方

特徴量エンジニアリングでの外れ値処理(Phase 3で本格的に学ぶ)

# よく使われる3つの方法
# 1. 外れ値を除外(データ量が多い場合)
df_clean = df[(df['Age'] >= lower_bound) & (df['Age'] <= upper_bound)]

# 2. クリッピング(外れ値を上限・下限に丸める)
df['Age_clipped'] = df['Age'].clip(lower=lower_bound, upper=upper_bound)

# 3. 変換(対数変換で外れ値の影響を緩和)
df['Age_log'] = np.log1p(df['Age'])

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
&and を混同Python の and は論理演算子pandas フィルタでは必ず & と ` を使う(and/or` はNG)
外れ値は必ず除外すべき「外れ値 = ゴミ」という誤解外れ値が重要な情報を持つケースもある(不正取引検出など)
.std() の デフォルトddof=1(不偏分散)vs. ddof=0(標本分散)pandas はデフォルト ddof=1(母集団が未知のサンプルを分析するとき)

🚀 次のステップ

  • 発展: Zスコア法((x - mean) / std が 3以上なら外れ値)で同じデータを検出し、IQR法と結果を比較する
  • 次回予告: 確率の基礎(確率とは何か、Kaggleで役に立つ確率の直感)

🎯 自己評価

自分の回答

気づき・メモ