Day 002 — 記述統計入門(平均・中央値・最頻値)

2026-04-10 白 / Phase 1 コーディング 記述統計入門

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

外れ値が「平均」を狂わせる

年収データを考えてください。

[300万, 320万, 310万, 290万, 305万, 1億]

この6人の平均は約1,754万円。でも実態に合っていませんよね?

1億という「外れ値」が平均を大きく引き上げています。

このとき中央値(並べたときの真ん中)を使うと307.5万円となり、実態に近い。

これがKaggleでも重要な知識です。特徴量に外れ値があると、平均ベースの処理が壊れるので、

まず「平均と中央値がどれくらい離れているか」で外れ値の存在を疑います。

NumPy vs Pandas どちらを使う?

場面推奨
数値配列だけnumpy
表形式データ(列・行あり)pandas

実際のKaggleでは pandas DataFrame を使うことが多いです。


📝 問題

以下のTitanicの年齢データ(一部)があります。

import numpy as np
import pandas as pd

ages = [22, 38, 26, 35, 35, None, 54, 2, 27, 14,
        4, 58, 20, 39, 14, 55, 2, None, 31, 35]

タスク1: ages リストから None を除いた有効なデータ数を求めてください。

タスク2: 有効なデータの 平均・中央値・最頻値 を求めてください(pandasを使う)。

タスク3: 平均と中央値の差が 5 以上の場合は「外れ値の可能性あり」、未満なら「分布は均一」と出力してください。

期待する出力例(値は自分で確かめること):

▶ 出力を見る
有効データ数: XX件
平均: XX.XX
中央値: XX.X
最頻値: XX
→ 分布は均一(または外れ値の可能性あり)

🔍 ヒント(段階的開示)

ヒント1(方向性)

None を除くには dropna()pd.Series の性質を使う。pandasはNoneをNaNとして扱う。

ヒント2(アプローチ)
s = pd.Series(ages)
s_clean = s.dropna()  # NaNを除去
s_clean.mean()        # 平均
s_clean.median()      # 中央値
s_clean.mode()[0]     # 最頻値(mode()はSeriesで返る)
ヒント3(コード骨格)
s = pd.Series(ages)
s_clean = s.dropna()

mean_val = s_clean.mean()
median_val = s_clean.median()
mode_val = s_clean.mode()[0]

print(f"有効データ数: {len(s_clean)}件")
print(f"平均: {mean_val:.2f}")
print(f"中央値: {median_val}")
print(f"最頻値: {mode_val}")

if abs(mean_val - median_val) >= 5:
    print("→ 外れ値の可能性あり")
else:
    print("→ 分布は均一")

模範解答

import numpy as np
import pandas as pd

ages = [22, 38, 26, 35, 35, None, 54, 2, 27, 14,
        4, 58, 20, 39, 14, 55, 2, None, 31, 35]

s = pd.Series(ages)
s_clean = s.dropna()

mean_val = s_clean.mean()
median_val = s_clean.median()
mode_val = s_clean.mode()[0]

print(f"有効データ数: {len(s_clean)}件")
print(f"平均: {mean_val:.2f}")
print(f"中央値: {median_val}")
print(f"最頻値: {mode_val}")

if abs(mean_val - median_val) >= 5:
    print("→ 外れ値の可能性あり")
else:
    print("→ 分布は均一")

出力:

▶ 出力を見る
有効データ数: 18件
平均: 28.61
中央値: 28.5
最頻値: 2
→ 分布は均一

🪜 Step-by-Step 解説

1
PandasのSeriesに変換する
s = pd.Series(ages)

リストをpandasの1次元データ構造「Series」に変換します。

None は自動的に NaN(Not a Number)として扱われます。

2
欠損値を除去する
s_clean = s.dropna()

dropna() でNaNを含む要素を取り除きます。

20件中2件が None → 18件の有効データになります。

3
統計量を計算する
mean_val = s_clean.mean()      # 全データの合計 / データ数
median_val = s_clean.median()  # ソートして真ん中の値
mode_val = s_clean.mode()[0]   # 最も多く登場する値

mode() は複数の最頻値が存在する場合にSeriesで返るので、[0] で最初の値を取得します。

4
外れ値判定
if abs(mean_val - median_val) >= 5:

平均と中央値が大きく離れているほど、データが偏っている(外れ値がある)サインです。

今回の差は 28.61 - 28.5 = 0.11 → 均一な分布。


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

なぜ mode() は [0] が必要か?

最頻値が複数ある場合(例: 2も35も同じ回数登場するとき)、pandasは全部返します。

mode() の結果は Series 型なので、最初の1つだけほしい場合は [0] でインデックスアクセスします。

今回のデータの最頻値が「2」なのはなぜ?

データを確認すると 2 が2回、35 が3回、14 が2回登場します。

最も多い 35 が最頻値になります(実際に実行して確かめてみてください)。


🏆 Kaggleでの実践的な使い方

Titanicコンペでは年齢(Age列)に欠損値が約20%あります。

欠損値を埋める(補完する)ときに「何で補完するか」が重要な選択になります:

  • 平均で補完: 外れ値の影響を受けやすい
  • 中央値で補完: 外れ値に強い → Titanicでは中央値補完が鉄板
df['Age'].fillna(df['Age'].median(), inplace=True)

このような「実際の処理」に今日の知識が直結します。


⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
mode() の結果をそのまま数値として使うSeriesとScalarの違いを意識していないmode()[0] で最初の値を取り出す
Noneを手動でフィルタリングするpandasの機能を知らないdropna() を使う
欠損を0で埋める「空欄=0」と思いがち0は「ゼロ歳」という意味になってしまう
平均≒中央値と思い込む均一なデータしか見たことがない外れ値があると大きくズレる

🚀 次のステップ

  • 発展: 外れ値を IQR法(四分位範囲) で検出する(Phase 1 テーマ2へ)
  • 次回予告: 分散・標準偏差 — データの「ばらつき」を数値で表す

🎯 自己評価

自分の回答

気づき・メモ