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

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

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

前回の復習(理論)

  • 分散: データが平均からどれくらい「バラついているか」の指標
  • 標準偏差: 分散の平方根。元のデータと同じ単位で使えるため実用的
  • 外れ値(Outlier): 他のデータから大きく外れた値。モデルの精度を下げる原因になる

Pythonでどう計算するか

import numpy as np
import pandas as pd

data = [10, 12, 11, 13, 10, 9, 11, 100]  # 100 が外れ値

# numpy を使った計算
mean = np.mean(data)       # 平均
std = np.std(data)         # 標準偏差(母標準偏差)
variance = np.var(data)    # 分散

# pandas を使った計算(サンプル標準偏差 = ddof=1がデフォルト)
series = pd.Series(data)
print(series.std())        # Pandasは不偏標準偏差(ddof=1)
print(series.var())        # 不偏分散

外れ値の検出: IQR法

IQR(四分位範囲) = 第3四分位数(Q3) - 第1四分位数(Q1)

外れ値の基準:

  • 下限: Q1 - 1.5 × IQR より小さい値
  • 上限: Q3 + 1.5 × IQR より大きい値

この方法はKaggleで最もよく使われるシンプルな外れ値検出法。


📝 問題

以下のデータ(架空の商品価格データ)に対して、Pythonで次のタスクを実行せよ。

import numpy as np
import pandas as pd

prices = pd.Series([
    120, 115, 130, 125, 118, 122, 128, 119,
    121, 117, 124, 126, 123, 115, 129, 122,
    500,   # 外れ値1
    3,     # 外れ値2
    121, 120
])

タスク1: 基本統計量の計算

以下を計算して出力せよ:

  • 平均・中央値・標準偏差・分散
  • 最小値・最大値・四分位数(Q1, Q2, Q3)

タスク2: IQR法で外れ値を検出

IQR法を用いて外れ値を検出し、「何番目(インデックス)のデータが外れ値か」と「その値」を表示せよ。

タスク3: 外れ値を除いた統計量の比較

外れ値を除いた新しいデータで平均・標準偏差を再計算し、除く前と除いた後を比較表示せよ。

出力例のイメージ:

▶ 出力を見る
=== 基本統計量 ===
平均: XX.X
中央値: XXX.X
標準偏差: XX.X
...

=== 外れ値検出(IQR法)===
外れ値インデックス: [XX, XX]
外れ値の値: [XXX, X]

=== 外れ値除去 前後の比較 ===
        除去前    除去後
平均    XXX.X    XXX.X
標準偏差  XX.X     X.X

🔍 ヒント(段階的開示)

ヒント1(方向性)
  • pd.Series.describe() で一気に基本統計量を確認できる
  • IQR = Q3 - Q1 を計算してから上限・下限を求める
  • 外れ値かどうかは条件式(series > upper_bound)でフィルタリングできる
ヒント2(アプローチ)
Q1 = prices.quantile(0.25)
Q3 = prices.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
ヒント3(コード骨格)
# 外れ値のインデックスを取得
outlier_mask = (prices < lower) | (prices > upper)
outlier_indices = prices[outlier_mask].index.tolist()
outlier_values = prices[outlier_mask].tolist()

# 外れ値を除いたデータ
prices_clean = prices[~outlier_mask]

模範解答

import numpy as np
import pandas as pd

prices = pd.Series([
    120, 115, 130, 125, 118, 122, 128, 119,
    121, 117, 124, 126, 123, 115, 129, 122,
    500,   # 外れ値1
    3,     # 外れ値2
    121, 120
])

# === タスク1: 基本統計量 ===
print("=== 基本統計量 ===")
print(f"平均:     {prices.mean():.1f}")
print(f"中央値:   {prices.median():.1f}")
print(f"標準偏差: {prices.std():.1f}")
print(f"分散:     {prices.var():.1f}")
print(f"最小値:   {prices.min()}")
print(f"最大値:   {prices.max()}")
print(f"Q1:       {prices.quantile(0.25)}")
print(f"Q2:       {prices.quantile(0.50)}")
print(f"Q3:       {prices.quantile(0.75)}")

# === タスク2: IQR法による外れ値検出 ===
Q1 = prices.quantile(0.25)
Q3 = prices.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR

print(f"\n=== 外れ値検出(IQR法)===")
print(f"IQR: {IQR:.1f} | 下限: {lower:.1f} | 上限: {upper:.1f}")

outlier_mask = (prices < lower) | (prices > upper)
outlier_indices = prices[outlier_mask].index.tolist()
outlier_values = prices[outlier_mask].tolist()

print(f"外れ値インデックス: {outlier_indices}")
print(f"外れ値の値:         {outlier_values}")

# === タスク3: 外れ値除去前後の比較 ===
prices_clean = prices[~outlier_mask]

print("\n=== 外れ値除去 前後の比較 ===")
comparison = pd.DataFrame({
    '除去前': [prices.mean(), prices.std(), len(prices)],
    '除去後': [prices_clean.mean(), prices_clean.std(), len(prices_clean)]
}, index=['平均', '標準偏差', 'データ数'])

print(comparison.round(1))

🪜 Step-by-Step 解説

1
基本統計量の計算
prices.mean()    # 平均: 合計 ÷ 個数
prices.median()  # 中央値: データを並べたとき真ん中の値
prices.std()     # 標準偏差(不偏、ddof=1がデフォルト)
prices.var()     # 分散
prices.quantile(0.25)  # Q1 = 下から25%の位置の値

重要: pandasstd() はデフォルトで不偏標準偏差(データが「標本」の場合)。

numpystd() はデフォルトで母標準偏差(データが「全体」の場合)。

Kaggleでは通常pandasを使うので std() = 不偏標準偏差を覚えておこう。

2
IQR法の仕組み
データを小さい順に並べた場合:
│ 25% │   50%    │   75% │
  ↑Q1             ↑Q3
  └──────IQR──────┘

外れ値の判定境界:
  下限 = Q1 - 1.5×IQR
  上限 = Q3 + 1.5×IQR

直感: 「データの中心付近の広がりの1.5倍を超えて離れたら外れ値」という考え方。

3
Boolean Indexingで外れ値を除外
outlier_mask = (prices < lower) | (prices > upper)
# → True/FalseのSeries: [False, False, ..., True, True, ...]

prices_clean = prices[~outlier_mask]  # ~ は「NOT」(反転)

~ (チルダ)は「条件を反転させる」演算子。外れ値=True を False にして、外れ値以外を抽出。


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

なぜ1.5倍なの?

1.5 という数字はジョン・テューキーが1977年に提案した経験則。

「正規分布に従うデータなら、この範囲外に入る確率は約0.7%」という性質に基づく。

100%正確である必要はなく、「大まかに外れてそうな値を捕まえる実用的な道具」と考えよう。

中央値と平均の違いが重要な場面

今回のデータで: 平均 ≈ 136(外れ値500に引っ張られる)、中央値 = 121(外れ値の影響が少ない)

→ 外れ値があるときは中央値の方が「典型的な値」を表す


🏆 Kaggleでの実践的な使い方

# コンペでよく使うパターン: 外れ値を除去してからモデルを学習
train = pd.read_csv('train.csv')

# 目的変数の外れ値を除去
Q1 = train['price'].quantile(0.25)
Q3 = train['price'].quantile(0.75)
IQR = Q3 - Q1
train_clean = train[
    (train['price'] >= Q1 - 1.5 * IQR) &
    (train['price'] <= Q3 + 1.5 * IQR)
]

print(f"除去前: {len(train)}行 → 除去後: {len(train_clean)}行")

外れ値処理はEDA(探索的データ分析)の最初のステップとして必須。

Kaggle上位解法では外れ値をどう扱うかが大きく順位に影響することも多い。


⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
外れ値は「間違いデータ」だから必ず除去する外れ値=ノイズという思い込み外れ値が正しい値の場合もある(富豪の年収データなど)。除去すべきかは文脈で判断
numpy.std() と pandas.std() が同じと思う両方 "std()" という名前numpy=母標準偏差(ddof=0)、pandas=不偏標準偏差(ddof=1)。値が微妙に違う
1.5×IQR が唯一の正解と思う1.5が一般的すぎる厳格にしたい場合は3.0×IQR、緩くしたい場合は1.0×IQR など調整できる

🚀 次のステップ

  • 発展: Z-score法(平均から標準偏差の3倍以上離れた値を外れ値とする方法)を試してみる
  • 次回予告: 確率の基礎(確率とは何か・事象と確率)

🎯 自己評価

自分の回答

気づき・メモ