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

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

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

標準偏差の「意味」をもう一度確認

標準偏差は「データのバラつき具合」を示す数値です。

  • 標準偏差が小さい → データが平均の近くに集まっている(バラつき少)
  • 標準偏差が大きい → データが広く散らばっている(バラつき大)

外れ値とは?

外れ値(Outlier)は「他のデータから大きく外れた値」のことです。

よく使われる判定方法:

  1. IQR法(四分位範囲): Q1-1.5×IQR より小さい / Q3+1.5×IQR より大きい値
  2. Zスコア法: 平均から標準偏差の3倍以上離れた値(|Z| > 3)
Zスコア = (データ値 - 平均) ÷ 標準偏差

Zスコアが「3以上」なら「平均から3σ(シグマ)以上離れている」= 通常では0.3%以下の確率しか出ない値。


📝 問題

以下のコードを完成させ、外れ値を検出して除去してください。

import numpy as np
import pandas as pd

# テストデータ(サラリーデータ・単位: 万円)
np.random.seed(42)
salary = np.random.normal(loc=500, scale=50, size=100).tolist()
salary.extend([5, 2000, 1])  # 外れ値を追加
salary = pd.Series(salary)

# ── タスク1 ──
# salary の平均・標準偏差・中央値を計算して表示する

mean_val = ___
std_val = ___
median_val = ___
print(f"平均: {mean_val:.1f}, 標準偏差: {std_val:.1f}, 中央値: {median_val:.1f}")

# ── タスク2 ──
# Zスコア法で外れ値を検出する(|Zスコア| > 3 のものを外れ値とする)

z_scores = ___
outliers = salary[___]  # 外れ値を抽出

print(f"\n外れ値の数: {len(outliers)}")
print(f"外れ値: {outliers.values}")

# ── タスク3 ──
# 外れ値を除去したデータで平均と標準偏差を再計算する

salary_clean = salary[___]  # 外れ値以外を抽出

print(f"\n除去後 → 平均: {salary_clean.mean():.1f}, 標準偏差: {salary_clean.std():.1f}")

期待出力(例):

▶ 出力を見る
平均: 490.5, 標準偏差: 176.2, 中央値: 499.8

外れ値の数: 3
外れ値: [   5.  2000.     1.]

除去後 → 平均: 500.3, 標準偏差: 49.8

🔍 ヒント(段階的開示)

ヒント1(方向性)

numpy と pandas の基本的な統計関数(.mean(), .std())を使います。Zスコアは「各値から平均を引いて標準偏差で割る」だけです。

ヒント2(アプローチ)
# Zスコアの計算式
z = (salary - salary.mean()) / salary.std()

# 外れ値の条件
abs(z) > 3
ヒント3(コード骨格)
mean_val = salary.mean()
std_val = salary.std()
median_val = salary.median()

z_scores = (salary - mean_val) / std_val
outliers = salary[abs(z_scores) > 3]

salary_clean = salary[abs(z_scores) <= 3]

模範解答

import numpy as np
import pandas as pd

np.random.seed(42)
salary = np.random.normal(loc=500, scale=50, size=100).tolist()
salary.extend([5, 2000, 1])
salary = pd.Series(salary)

# タスク1: 基本統計量
mean_val = salary.mean()
std_val = salary.std()
median_val = salary.median()
print(f"平均: {mean_val:.1f}, 標準偏差: {std_val:.1f}, 中央値: {median_val:.1f}")

# タスク2: Zスコア法で外れ値を検出
z_scores = (salary - mean_val) / std_val
outliers = salary[abs(z_scores) > 3]
print(f"\n外れ値の数: {len(outliers)}")
print(f"外れ値: {outliers.values}")

# タスク3: 外れ値を除去して再計算
salary_clean = salary[abs(z_scores) <= 3]
print(f"\n除去後 → 平均: {salary_clean.mean():.1f}, 標準偏差: {salary_clean.std():.1f}")

🪜 Step-by-Step 解説

1
基本統計量の計算
mean_val = salary.mean()    # pandas Series の平均
std_val = salary.std()      # 標準偏差(ddof=1、不偏標準偏差がデフォルト)
median_val = salary.median() # 中央値

外れ値がある場合、平均は大きく影響を受けるが中央値は影響を受けにくい

→ 実際の出力で 平均 490.5 vs 中央値 499.8 のズレがその証拠。

2
Zスコアの計算
z_scores = (salary - mean_val) / std_val

これは「各データが平均から何σ離れているか」を表します。

  • 正規分布では 99.7% のデータが |Z| ≤ 3 の範囲に収まる
  • |Z| > 3 のデータは「統計的に異常に離れている」= 外れ値候補
3
外れ値の抽出と除去
outliers = salary[abs(z_scores) > 3]       # 外れ値
salary_clean = salary[abs(z_scores) <= 3]  # 外れ値以外

abs() で絶対値を取ることで、平均より高い外れ値も低い外れ値も両方検出できます。


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

なぜ「3σ」が閾値なの?

正規分布(データが釣り鐘型に分布している場合)では:

  • 平均 ± 1σ の範囲 → データの約 68% が入る
  • 平均 ± 2σ の範囲 → データの約 95% が入る
  • 平均 ± 3σ の範囲 → データの約 99.7% が入る

つまり「3σを超える値は 1000件に3件以下」という確率的に珍しい値 → 外れ値と判断する。


🏆 Kaggleでの実践的な使い方

実際のKaggleコンペでは:

# 外れ値を IQR 法で検出(より頑健な方法)
Q1 = df['price'].quantile(0.25)
Q3 = df['price'].quantile(0.75)
IQR = Q3 - Q1
df_clean = df[(df['price'] >= Q1 - 1.5 * IQR) & (df['price'] <= Q3 + 1.5 * IQR)]

House Prices コンペ(実例): 住宅価格の外れ値を除去することで、線形回帰モデルのRMSEが大幅に改善するケースが多い。


⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
外れ値は常に除去すべきデータの正確さへの過信ドメイン知識で判断。本物の高額物件なら残す
abs(z_scores) > 3abs を忘れる正の外れ値だけ検出してしまう両側(高すぎ・低すぎ)を検出するため必須
std() の ddof を意識しないnumpy.std はデフォルト ddof=0(母標準偏差)、pandas.std はddof=1(不偏)サンプルデータは pandas の .std() を使う

🚀 次のステップ

  • 発展問題: IQR 法で同じデータの外れ値を検出し、Zスコア法と結果を比較する
  • 次回予告: 確率の基礎(確率とは何か)

🎯 自己評価

自分の回答

気づき・メモ