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

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

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

昨日(Day007)は理論で分散・標準偏差・外れ値の概念を学んだ。

今日は Pythonで実際に計算・検出する コーディング問題。

よく使う関数

import numpy as np

data = [2, 4, 4, 4, 5, 5, 7, 9]

np.mean(data)     # 平均
np.var(data)      # 分散(標本分散、N で割る)
np.std(data)      # 標準偏差(同上)
np.percentile(data, 25)  # Q1(第1四分位数)
np.percentile(data, 75)  # Q3(第3四分位数)

IQR法の実装フロー

1. Q1 = np.percentile(data, 25)
2. Q3 = np.percentile(data, 75)
3. IQR = Q3 - Q1
4. 下限 = Q1 - 1.5 * IQR
5. 上限 = Q3 + 1.5 * IQR
6. 範囲外のデータ = 外れ値

📝 問題

以下のコードを完成させてください。

import numpy as np
import pandas as pd

# 不動産価格データ(万円)
prices = [
    1500, 1800, 2000, 1700, 1600,
    1900, 2100, 1850, 1750, 1650,
    2200, 1550, 1950, 8500,  # 8500 は外れ値
    1700, 1800, 2050, 1900, 1600, 1750
]

# --- タスク1: 基本統計量を計算 ---
mean_price = ___
std_price = ___
var_price = ___

print(f"平均: {mean_price:.0f}万円")
print(f"標準偏差: {std_price:.0f}万円")
print(f"分散: {var_price:.0f}万円²")

# --- タスク2: 3σルールで外れ値を検出 ---
lower_3sigma = ___
upper_3sigma = ___
outliers_3sigma = [p for p in prices if ___ or ___]

print(f"\n3σルール外れ値: {outliers_3sigma}")

# --- タスク3: IQR法で外れ値を検出 ---
q1 = ___
q3 = ___
iqr = ___
lower_iqr = ___
upper_iqr = ___
outliers_iqr = [p for p in prices if ___ or ___]

print(f"IQR法外れ値: {outliers_iqr}")

# --- タスク4: 外れ値を除いた平均を計算 ---
clean_prices = [p for p in prices if ___]  # IQR法で外れ値除去
mean_clean = ___

print(f"\n外れ値除去後の平均: {mean_clean:.0f}万円")
print(f"外れ値による歪み: {mean_price - mean_clean:.0f}万円")

🔍 ヒント(段階的開示)

ヒント1(方向性)

np.mean(), np.std(), np.percentile() の基本的な使い方を確認する。外れ値検出は「閾値の計算」→「リスト内包表記でフィルタ」の2ステップ。

ヒント2(アプローチ)
  • タスク2の閾値: mean ± 3 * std
  • タスク3の閾値: q1 - 1.5 iqrq3 + 1.5 iqr
  • タスク4の clean_prices は「IQRの範囲内」だけを残す
ヒント3(コード骨格)
# タスク1
mean_price = np.mean(prices)
std_price = np.std(prices)
var_price = np.var(prices)

# タスク2
lower_3sigma = mean_price - 3 * std_price
upper_3sigma = mean_price + 3 * std_price
outliers_3sigma = [p for p in prices if p < lower_3sigma or p > upper_3sigma]

# タスク3
q1 = np.percentile(prices, 25)
q3 = np.percentile(prices, 75)
iqr = q3 - q1
lower_iqr = q1 - 1.5 * iqr
upper_iqr = q3 + 1.5 * iqr

模範解答

import numpy as np

prices = [
    1500, 1800, 2000, 1700, 1600,
    1900, 2100, 1850, 1750, 1650,
    2200, 1550, 1950, 8500,
    1700, 1800, 2050, 1900, 1600, 1750
]

# タスク1: 基本統計量
mean_price = np.mean(prices)
std_price = np.std(prices)
var_price = np.var(prices)

print(f"平均: {mean_price:.0f}万円")
print(f"標準偏差: {std_price:.0f}万円")
print(f"分散: {var_price:.0f}万円²")

# タスク2: 3σルール
lower_3sigma = mean_price - 3 * std_price
upper_3sigma = mean_price + 3 * std_price
outliers_3sigma = [p for p in prices if p < lower_3sigma or p > upper_3sigma]

print(f"\n3σルール外れ値: {outliers_3sigma}")
print(f"  下限: {lower_3sigma:.0f}万円, 上限: {upper_3sigma:.0f}万円")

# タスク3: IQR法
q1 = np.percentile(prices, 25)
q3 = np.percentile(prices, 75)
iqr = q3 - q1
lower_iqr = q1 - 1.5 * iqr
upper_iqr = q3 + 1.5 * iqr
outliers_iqr = [p for p in prices if p < lower_iqr or p > upper_iqr]

print(f"\nIQR法外れ値: {outliers_iqr}")
print(f"  Q1: {q1:.0f}, Q3: {q3:.0f}, IQR: {iqr:.0f}")
print(f"  下限: {lower_iqr:.0f}万円, 上限: {upper_iqr:.0f}万円")

# タスク4: 外れ値除去後の平均
clean_prices = [p for p in prices if lower_iqr <= p <= upper_iqr]
mean_clean = np.mean(clean_prices)

print(f"\n外れ値除去後の平均: {mean_clean:.0f}万円")
print(f"外れ値による歪み: {mean_price - mean_clean:.0f}万円")

実行結果(近似値):

▶ 出力を見る
平均: 2278万円
標準偏差: 1505万円
分散: 2265025万円²

3σルール外れ値: []          ← 外れ値8500を検出できない!
  下限: -3237万円, 上限: 7793万円

IQR法外れ値: [8500]         ← IQRは正しく検出
  Q1: 1650, Q3: 1925, IQR: 275
  下限: 1237万円, 上限: 2337万円

外れ値除去後の平均: 1803万円
外れ値による歪み: 475万円

🪜 Step-by-Step 解説

1
3σルールが外れ値 8500 を検出できない理由
mean = 2278   # 外れ値8500に引っ張られて平均が上昇
std = 1505    # 外れ値があるため標準偏差も大きくなる
upper = mean + 3 * std = 2278 + 4515 = 6793  # 閾値が7800に広がる
# → 8500 は超えるが、3σルールの信頼性が低下している

これが「右歪みデータに3σルールが弱い」理由。

2
IQR法が正しく検出できる理由
q1 = 1650   # 外れ値の影響を受けない(中央50%を使っているから)
q3 = 1925
iqr = 275
upper_iqr = 1925 + 1.5 * 275 = 2337  # 妥当な閾値
# → 8500 >> 2337 なので外れ値として検出
3
外れ値 1個が平均を 475万円 引き上げている
外れ値あり平均: 2278万円
外れ値なし平均: 1803万円
差: 475万円(約26%の歪み)

これが「外れ値を除去してから分析する」ことの重要性。


🏆 Kaggleでの実践的な使い方

import pandas as pd
import numpy as np

df = pd.read_csv('train.csv')

def remove_outliers_iqr(df, column):
    """指定カラムのIQR外れ値を除去したDataFrameを返す"""
    q1 = df[column].quantile(0.25)
    q3 = df[column].quantile(0.75)
    iqr = q3 - q1
    lower = q1 - 1.5 * iqr
    upper = q3 + 1.5 * iqr
    mask = (df[column] >= lower) & (df[column] <= upper)
    return df[mask], df[~mask]

# House Pricesコンペの例
clean_df, outlier_df = remove_outliers_iqr(df, 'SalePrice')
print(f"外れ値除去: {len(outlier_df)}件 / 全{len(df)}件")

Kaggleでの判断:

  • 訓練データのみ外れ値を除去し、テストデータはそのまま使う
  • 除去する前に、その物件が「入力ミスか本物の高級物件か」をEDAで確認する

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
np.std()pd.Series.std() の結果が違う分母が N か N-1 か(標本 vs 不偏)np.std はデフォルトN(標本)、pandas はN-1(不偏)。引数 ddof で調整可
外れ値を全部削除するノイズと思い込むテストデータにも同様のデータがあれば残す必要がある
percentile(25)quantile(0.25) を混同関数名の違い同じ意味。numpy は percentile、pandas は quantile

🚀 次のステップ

  • 発展: log変換で外れ値の影響を軽減する(np.log1p(prices) で分布を正規化)
  • 次回予告: 確率の基礎(確率とは何か / 同様に確からしい)へ進む(テーマ3)

🎯 自己評価

自分の回答

気づき・メモ