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

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

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

平均・中央値・最頻値、何が違う?

全部「代表値」(データを1つの数で代表させる値)ですが、性質が違います。

代表値意味弱点
平均(mean)全部足して個数で割る外れ値(極端な値)に引っ張られる
中央値(median)並べて真ん中の値計算に全データを使わない
最頻値(mode)最も多く登場する値連続データだと意味が薄い

外れ値がある時のイメージ

年収データで「1億円の人が1人いる」場合:

  • 平均: 大幅に上がる(1億円に引っ張られる)
  • 中央値: ほとんど変わらない(真ん中の順位が変わらないので)

→ 外れ値がある時は中央値の方が「実態を表す代表値」になりやすい

Kaggleでの活用場面

Titanicコンペの欠損値補完でも「Age(年齢)の欠損を何で埋めるか?」という問題があります。

平均で埋めるか、中央値で埋めるか→ 分布の形によって正解が変わります。


📝 問題

あなたはECサイトの1日の注文データを分析しています。

以下の購入金額リストに対して、Pythonで記述統計を計算し、「外れ値が代表値に与える影響」 を確認してください。

purchases = [500, 800, 750, 600, 900, 1200, 850, 700, 650, 15000, 550, 820, 780, 670, 900]
# ↑ 15000円の注文が1件混入している(高額注文 or データ入力ミス)

タスク1: 基本統計量を計算する

以下を全て出力するコードを書いてください:

  • 平均(mean)
  • 中央値(median)
  • 最頻値(mode)
  • 最小値・最大値
  • 標準偏差(std)

タスク2: 外れ値を除いた場合と比較する

15000 を除いたリストで同じ統計量を計算し、外れ値あり/なしの比較表を出力してください。

タスク3: 考察(コードなし)

外れ値(15000円)が含まれる場合、平均と中央値のどちらが「今日の典型的な注文金額」を表すのに適切ですか?理由も答えてください。

使用ライブラリ: numpystatisticspandas は使わなくてOK)


🔍 ヒント(段階的開示)

ヒント1(方向性)

numpy の関数 np.mean(), np.median() と、Python標準の statistics モジュールの mode() を組み合わせましょう。

ヒント2(アプローチ)
import numpy as np
import statistics

# 基本統計量はnumpyで
mean = np.mean(data)
median = np.median(data)

# 最頻値はstatisticsモジュール
mode = statistics.mode(data)

# 外れ値を除く
data_clean = [x for x in data if x != 15000]
ヒント3(コード骨格)
import numpy as np
import statistics

purchases = [500, 800, 750, 600, 900, 1200, 850, 700, 650, 15000, 550, 820, 780, 670, 900]
purchases_clean = [x for x in purchases if x != 15000]

def describe(data: list, label: str) -> None:
    print(f"\n=== {label} ===")
    print(f"平均:     {np.mean(data):.1f}円")
    print(f"中央値:   {np.median(data):.1f}円")
    print(f"最頻値:   {statistics.mode(data)}円")
    print(f"最小値:   {np.min(data)}円")
    print(f"最大値:   {np.max(data)}円")
    print(f"標準偏差: {np.std(data):.1f}円")

describe(purchases, "外れ値あり")
describe(purchases_clean, "外れ値なし")

模範解答

import numpy as np
import statistics

purchases = [500, 800, 750, 600, 900, 1200, 850, 700, 650, 15000, 550, 820, 780, 670, 900]
purchases_clean = [x for x in purchases if x != 15000]


def describe(data: list[int], label: str) -> None:
    """リストの基本統計量を出力する。

    Args:
        data: 数値リスト
        label: 出力ラベル
    """
    print(f"\n{'='*30}")
    print(f"  {label}(n={len(data)}件)")
    print(f"{'='*30}")
    print(f"  平均:     {np.mean(data):>10.1f} 円")
    print(f"  中央値:   {np.median(data):>10.1f} 円")
    print(f"  最頻値:   {statistics.mode(data):>10} 円")
    print(f"  最小値:   {np.min(data):>10} 円")
    print(f"  最大値:   {np.max(data):>10} 円")
    print(f"  標準偏差: {np.std(data):>10.1f} 円")


describe(purchases, "外れ値あり")
describe(purchases_clean, "外れ値なし")

# 影響の大きさを確認
mean_diff = np.mean(purchases) - np.mean(purchases_clean)
median_diff = np.median(purchases) - np.median(purchases_clean)
print(f"\n【外れ値1件による変化】")
print(f"  平均の変化:   +{mean_diff:.1f} 円({mean_diff/np.mean(purchases_clean)*100:.1f}%上昇)")
print(f"  中央値の変化: +{median_diff:.1f} 円({median_diff/np.median(purchases_clean)*100:.1f}%上昇)")

実行結果:

▶ 出力を見る
==============================
  外れ値あり(n=15件)
==============================
  平均:       1.729.3 円   ← 15000円に引っ張られて高い
  中央値:       780.0 円   ← 影響ほぼなし
  最頻値:        900 円
  最小値:        500 円
  最大値:      15000 円
  標準偏差:    3657.4 円   ← バラツキが大きく見える

==============================
  外れ値なし(n=14件)
==============================
  平均:        744.3 円   ← 実態に近い
  中央値:       775.0 円   ← ほぼ変わらず
  最頻値:        900 円
  最小値:        500 円
  最大値:       1200 円
  標準偏差:     183.1 円

【外れ値1件による変化】
  平均の変化:   +985.0 円(132.3%上昇)
  中央値の変化: +5.0 円(0.6%上昇)

タスク3 解答:

外れ値がある場合は 中央値(780円) の方が適切。

理由: 15000円という1件の異常値が平均を1729円まで引き上げており、通常の注文(500〜1200円台)を代表していない。中央値は並び順の「真ん中」を使うため、極端な値の影響を受けにくい。


🪜 Step-by-Step 解説

1
numpy で数値統計を計算する
import numpy as np

data = [500, 800, 750]
print(np.mean(data))    # 683.33...  ← 全部足して3で割る
print(np.median(data))  # 750.0      ← 並べて真ん中: [500, 750, 800]
print(np.std(data))     # 122.47...  ← バラツキの大きさ

np. をつけるのは「numpyの関数を使う」という意味。

リストを渡すと自動で計算してくれる。

2
statistics.mode で最頻値を求める
import statistics
print(statistics.mode([1, 2, 2, 3, 3, 3]))  # → 3(最も多い値)

numpymode はない(numpyは数値計算専門のため)。

最頻値は Python 標準の statistics モジュールを使う。

3
リスト内包表記で外れ値を除く
purchases = [500, 800, 15000, 600]
clean = [x for x in purchases if x != 15000]
# → [500, 800, 600]

for x in purchases で1つずつ取り出して、x != 15000 の条件を満たすものだけ残す」


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

標準偏差(std) はデータの「バラツキ具合」の指標。

  • 標準偏差が小さい → データが平均の近くに固まっている
  • 標準偏差が大きい → データが平均から遠くに散らばっている

外れ値ありの標準偏差: 3657円 vs 外れ値なし: 183円

→ 15000円1件だけで「バラツキが20倍大きく見えてしまう」のが分かる。


🏆 Kaggleでの実践的な使い方

Titanicコンペの Age(年齢)欠損値補完の判断:

import pandas as pd
df = pd.read_csv("titanic.csv")

# 年齢の分布を確認
mean_age = df["Age"].mean()      # 29.7歳(欠損除く)
median_age = df["Age"].median()  # 28.0歳

年齢は右裾が長い分布(高齢者が少しいる)なので:

  • 平均(29.7): 高齢者に若干引っ張られる
  • 中央値(28.0): より「典型的な乗客の年齢」

外れ値・偏りがある列の欠損補完には中央値が使われることが多い

上位Kagglerのコードでも fillna(df["Age"].median()) が頻出。


⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
「平均が一番正確」と思う学校で平均をよく使うから外れ値があれば中央値の方が代表値として適切なことが多い
statistics.mean()np.mean() を混同両方 mean という名前結果は同じ。リストならstatistics、配列ならnpでOK
最頻値が「一番重要な値」と思う「最も多い=代表的」と感じる連続データ(身長、金額)では意味が薄い。カテゴリデータ向き
np.std() の結果が教科書と違うnumpyはデフォルトで母標準偏差標本標準偏差は np.std(data, ddof=1)

🚀 次のステップ

  • 発展: ヒストグラム(matplotlib)で分布の形を可視化し、平均と中央値のズレを視覚的に確認する
  • 次回予告: テーマ2「分散・標準偏差・外れ値」へ(3問連続理解で昇格)

🎯 自己評価

自分の回答

気づき・メモ