論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
箱ひげ図
Box Plot
中央値・四分位・外れ値をひと目で見せる図。複数群の分布比較に最適。
可視化箱ひげboxplotbox plot

🔖 キーワード索引(拡張)

箱ひげ図関連の重要語をクイックアクセス:

五数要約 IQR(四分位範囲) ひげの長さ(1.5×IQR) 外れ値の自動判定 ノッチ箱ひげ バイオリン図 strip plot / swarm 多峰性が見えない サンプル数の隠蔽 外れ値の定義差 matplotlib boxplot seaborn violinplot plotly express scipy.stats.iqr

💡 30秒で分かる結論

🍰 まずはやさしく

データのまとめ役のような図です。

ばらつきや外れ値をひと目で知るために使います。

部活のメンバーの身長を比べる時に便利です。

図に込められた5つの要素について読みましょう。

👁️ 直感 — 箱ひげ図は「分布の5数要約 + 外れ値」を1枚で

箱ひげ図(box plot, box-and-whisker plot)は、 分布の中央値・四分位・外れ値を1つの図で表現する強力な可視化。 Tukey が1977年に体系化。

箱ひげ図の構造

5つの要素

  1. 箱の左辺(Q1):第1四分位、 下から25%の地点
  2. 箱の真ん中の太線(Q2 = 中央値):50%の地点
  3. 箱の右辺(Q3):第3四分位、 下から75%の地点
  4. ひげ(whiskers):Q1−1.5×IQR から Q3+1.5×IQR の範囲
  5. 点(fliers):ひげの外側の値 = 外れ値候補

SSDSE 食料費の場合:Q1 = 76.0、 中央値 = 79.9、 Q3 = 85.1、 IQR = 9.1千円。

💡 箱ひげ図1枚で 中央値・ばらつき・歪み・外れ値 がすべて分かります。 探索的データ分析(EDA)の主力ツール。

📍 あなたが今見ているもの

🍰 まずはやさしく

データの分布をシンプルに見せる図です。

グループごとの違いを比べるために使います。

地域ごとの食費の差を調べる時に役立ちます。

定義から実装までの6つの視点で解説します。

論文中に 「箱ひげ図」として登場する用語。

箱ひげ図 とは:中央値・四分位・外れ値をひと目で見せる図。複数群の分布比較に最適。

本ページでは「boxplot」を扱う。 統計データ分析コンペティション (2026) の教材で、 SSDSE-B-2026 (47 都道府県 × 複数年 × 100 超列) の実データを使った再現可能な学習を目指す。

「boxplot」は統計・データサイエンスの体系における重要概念のひとつ。 本ページは「定義・直感・数式・実装・落とし穴・関連手法」の 6 視点で構成され、 各視点は独立して読めるが順序通り読むと体系的な理解が得られる。

🎮 触って理解する — データを動かして箱ひげ図を再描画

下の左パネルはデータ点の並んだ「帯」です。 点をドラッグすると値が変わり、 空いた場所をタップ/クリックすると点が増え、 点をタップすると消えます(動かさなければ削除、 動かせば移動)。 プリセットには SSDSE-B-2026(2023 年・47 都道府県)の実測値を用意しました。 データを動かすたびに 五数要約(最小・Q1・中央値・Q3・最大)・IQR・1.5×IQR フェンス・外れ値が上のミニ箱ひげ図でリアルタイムに再描画され、 右のヒストグラムと 同じデータの 2 つの表現を並べて見比べられます。

上=箱ひげ図(箱=Q1〜Q3・太線=中央値・◆=平均・×=外れ値)/下=データ点(ドラッグで移動・タップで追加/削除)
同じデータのヒストグラム(青破線=中央値・緑実線=平均)

💡 体感ポイント: 「外れ値を1つ追加」を押すと、 極端な1点でも 中央値と IQR(箱)はほとんど動かないのに、 ◆平均は外れ値に引っ張られて移動します。 これが「中央値・IQR は頑健、 平均・標準偏差は外れ値に弱い」という頑健統計量の核心です。 逆に箱の中央付近の点を動かしても五数要約はほとんど変わらず、 箱ひげ図が分布の「骨格」だけを要約していることが分かります。 SSDSE 消費支出のプリセットでは 1 県(愛媛)が下側フェンスの外に落ち、 実データでも外れ値が自然に現れることを確認できます。

📖 この体験と直結する 5 つの論点
  1. 五数要約:最小・第1四分位 Q1・中央値・第3四分位 Q3・最大の 5 つで分布を要約。 平均やSDと違い、 分布の形(歪み・裾)を仮定せずに「どこにどれだけ散らばるか」を表す(=分位数ベースの記述)。
  2. IQR と外れ値:IQR = Q3 − Q1(中央 50% の幅)。 IQR を使い Q1 − 1.5×IQRQ3 + 1.5×IQR のフェンス外を「外れ値候補」とするのが Tukey ルール。 点を外へ動かすと、 フェンスと ×印がどう反応するかが見える。
  3. 頑健性中央値・IQR は外れ値に強い(頑健)が、 平均標準偏差分散は 1 点の極値で大きく動く。 箱ひげ図が EDA の主力なのは、 この頑健な要約を一目で与えるため。
  4. 複数群の比較:箱ひげ図の真価は群を横に並べたとき。 中央値の高さ・箱の重なり・裾の長さを比べれば、 平均だけでは見えない「ばらつきの差」や「外れ値の偏り」まで一望できる。
  5. ヒストグラム/バイオリン図との対比:右のヒストグラムは同じデータの密度を示す別表現。 箱ひげ図は要約に強いが多峰性(山が2つ)を隠す弱点があり、 分布の形まで見たいときはヒストグラムやバイオリン図(箱ひげ+カーネル密度推定)を併用する。

🎨 箱ひげ図の派生

🍰 まずはやさしく

箱ひげ図をアレンジした仲間たちです。

データの量や目的に合わせて使い分けます。

少人数のテスト結果を点で見せる方法などがあります。

より詳しく分析するための派生図を紹介します。

🎨 箱ひげ図の派生

① Strip plot(ストリッププロット)

個別のデータ点をひげの位置にずらして並べる。 サンプルサイズが小さい時に有効:

sns.stripplot(x='地域', y='食料費', data=df) sns.swarmplot(x='地域', y='食料費', data=df) # 重ならないように配置

② Notched box plot(くびれ箱ひげ図)

中央値の95%信頼区間を「くびれ」で表示。 群間の重なりで有意差が判断できる:

plt.boxplot(data, notch=True) # 2群のくびれが重ならない → 中央値に有意差あり(α=0.05)

③ Letter-value plot (boxenplot)

大量データ用の拡張箱ひげ図。 複数階層のパーセンタイルを表示:

sns.boxenplot(x='地域', y='食料費', data=df)

④ Beeswarm plot

密度が高い場所で点を横に広げて、 重ならないように配置するプロット。 小〜中規模データに最適。

箱ひげ図は「5 数要約(最小・Q1・中央・Q3・最大)を 1 つの箱で示す」。 群間比較に強く、 平均は出さない。 SSDSE-B-2026 で A1101 の箱ひげを描くと、 Q1=103.4 万、 中央=154.9 万、 Q3=263.7 万、 上ヒゲ閾値=504 万を超える東京〜北海道の 9 都道府県が「外れ値」点として描画される。

💡 箱ひげ図の読み方のコツ:箱の幅 = IQR は中央 50% のばらつき、 ひげの長さは「IQR × 1.5 以内」の範囲。 SSDSE-B-2026 で 47 都道府県の人口(A1101)を描くと、 東京・神奈川・大阪が明確に外れ値点として浮かび上がります。 これは異常値ではなく「分布の右裾」を示すサインです。

箱ひげ図 は「可視化」カテゴリの中核概念。 初めて触れる読者は、 まずこの「🎨 直感」セクションだけ通読し、 必要になった時点で「📐 数式」「🐍 Python」「⚠️ 落とし穴」へ戻る読み方が定着しやすいです。

📐 定義・数式 — 箱ひげ図

🍰 まずはやさしく

箱ひげ図を作るためのルールです。

正確な位置を計算するために使います。

スマホの利用時間のばらつきを数値で出します。

ひげの長さを決める数式について読みましょう。

直感の次は、 厳密な定義を確認します。 数式は言語の一種で、 一度書き慣れれば「言葉より速く伝えられる」便利な道具。 慣れていない方は、 各記号が何を表すかを下の「🔬 数式を言葉で読み解く」で 1 つずつ確認してください。

【箱ひげ図 の中心定義式】
$$ \text{whisker}_{\text{upper}} = Q_3 + 1.5 \cdot \text{IQR}, \;\; \text{IQR} = Q_3 - Q_1 $$
この式が「箱ひげ図」の骨格。 派生形・拡張形はここから生まれる。
📌 読み方のコツ:数式を見たら「左辺は何を定義しているか」「右辺の各項は何の合計・積・比か」を声に出して読み下してみる。 これだけで理解が大きく進みます。

🔬 数式を言葉で読み解く — 数式を「言葉」に翻訳

上の数式を眺めるだけでは身につかないので、 各記号がどんな役割を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。

左辺(結果側)
箱ひげ図 で定義したい量。 解釈の対象。 単位・スケールを必ず確認する。
右辺(構成要素)
観測できる入力変数(SSDSE-B-2026 でいえば A1101・L3221 など)と推定対象パラメータ(β, σ 等)の組合せ。
添字 i, j, t
i=サンプル(県)、 j=変数、 t=時点。 SSDSE-B-2026 は i ∈ {1..47} 県、 t ∈ {2008..2023}。
和記号 Σ
「足し合わせ」を表す。 添字 i が 1 から n まで動く範囲を明示するのが習慣。
期待値 E[·]、 分散 Var[·]
「ランダム変数の平均」と「ばらつき」。 SSDSE-B-2026 のような集計値でも、 標本誤差・年次変動の文脈で使える。
📚 補足:同じ記号でも分野・教科書によって意味が違うことがあります(例: $\hat{y}$ は予測値だが、 統計の文脈では推定量を意味することも)。 不明確なときは、 必ずその文書の記号定義表を確認しましょう。

🧮 SSDSE-B 実値計算 — 8 地方ブロックの「消費支出」を箱ひげで比較

47都道府県の「L3221 消費支出(二人以上の世帯、 円/月)」(2023 年度)を地方ブロックごとに集計し、 五数要約・IQR・外れ値を取り出して箱ひげ図を描く。

🎯 解説: 水平箱ひげ図(horizontal)でカテゴリ名が長い場合に視認性向上。 SSDSE-B-2026 の長い項目名(県名)を縦軸に配置。
📥 入力例: data/raw/SSDSE-B-2026.csv plt.boxplot(data, vert=False) または sns.boxplot(orient='h')
📤 実行例: 横向きの箱
  上から地方ブロック
  ラベルが見やすい

💬 読み方: カテゴリ数が多い(10 超)と縦向き表示は見づらい。 横向き + sorted で並べ替えれば順位も把握しやすい。 ラベル長 > 5 文字なら水平推奨。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()   # 2023 年度の 47 都道府県

def region(code):
    num = int(str(code).lstrip('R')) // 1000  # 'R01000' → 1
    if num == 1:            return '北海道'
    if 2  <= num <= 7:      return '東北'
    if 8  <= num <= 14:     return '関東'
    if 15 <= num <= 23:     return '中部'
    if 24 <= num <= 30:     return '近畿'
    if 31 <= num <= 35:     return '中国'
    if 36 <= num <= 39:     return '四国'
    return '九州沖縄'
df['地域'] = df['Code'].apply(region)

# 後続例で使う派生列(実在列から導出・リネーム)
df['高齢化率'] = df['A1303'] / df['A1101'] * 100
df['都市規模'] = np.where(df['A1101'] >= 2e6, '200万人以上', '200万人未満')
df = df.rename(columns={'L322101': '食料費', 'L322102': '住居費',
                        'L322108': '教育費'})

# 五数要約と IQR を取り出す
print(df.groupby('地域')['L3221']
        .describe(percentiles=[.25, .5, .75])
        .round(0))

# 箱ひげ図を描く
fig, ax = plt.subplots(figsize=(10, 5))
order = df.groupby('地域')['L3221'].median().sort_values().index
data = [df.loc[df['地域'] == r, 'L3221'] for r in order]
# matplotlib 3.9 以降、boxplot の labels= は tick_labels= に変わった
ax.boxplot(data, tick_labels=order, showmeans=True,
           meanprops=dict(marker='D', markerfacecolor='red'))
ax.set_ylabel('L3221 消費支出(二人以上の世帯、円/月)')
ax.set_title('地方ブロック別 消費支出の分布(SSDSE-B-2026, 2023)')
ax.grid(alpha=.3, axis='y')
plt.tight_layout(); plt.savefig('boxplot_region.png', dpi=140)

📤 実行結果(SSDSE-B-2026、 2023 年度の実測値)

count mean std min 25% 50% 75% max 地域 中国 5.0 292150.0 12932.0 272599.0 286970.0 294837.0 300973.0 305373.0 中部 9.0 305034.0 14787.0 276690.0 300221.0 301215.0 313991.0 327503.0 九州沖縄 8.0 279203.0 21741.0 251222.0 261182.0 276996.0 297060.0 309000.0 北海道 1.0 296888.0 NaN 296888.0 296888.0 296888.0 296888.0 296888.0 四国 4.0 276528.0 35879.0 223423.0 270956.0 290838.0 296411.0 301013.0 東北 6.0 294952.0 22720.0 263371.0 278698.0 302038.0 306775.0 322992.0 近畿 7.0 296754.0 26792.0 259437.0 275563.0 305586.0 314234.0 332663.0 関東 7.0 316509.0 21608.0 283599.0 306754.0 307817.0 333273.0 344092.0

典型的な観察例(2023 年度の実測): 関東ブロックは中央値 307,817 円と 8 ブロック中最高で、 最大は埼玉の 344,092 円。 九州沖縄は中央値 276,996 円と最も低い。 四国は最小の愛媛 223,423 円を含み標準偏差 35,879 円と最もばらつきが大きい。 同じ「中央値」でも分布形状が違うことが一目で分かる。 平均(赤ダイヤ)と中央値(オレンジ線)が乖離していれば歪度の存在を示唆(例:四国は平均 276,528 円 < 中央値 290,838 円で左に歪む)。

IQR ルールで外れ値を抽出

🎯 解説: グループ + サブグループの 2 層分類で箱ひげ図。 hue 引数でサブグループを色分け、 dodge=True で並列表示。 多次元比較に便利。
📥 入力例: data/raw/SSDSE-B-2026.csv x='地域', y='高齢化率'(A1303/A1101×100 で導出) hue='都市規模'(A1101 200万人以上/未満で導出)

📤 実行例: 地方ブロック × 都市規模 で 14 箱(北海道・四国は片側のみ) ほとんどのブロックで 200 万人未満の県の方が高齢化率高(例:東北 35.2 vs 29.2)

💬 読み方: hue で詳細な比較が可能だが、 箱数が増えると見づらい。 凡例を明示。 サブグループ間の関係性に着目するなら point plot や bar plot も検討。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def detect_outliers(s, k=1.5):
    q1, q3 = s.quantile(.25), s.quantile(.75)
    iqr = q3 - q1
    lo, hi = q1 - k*iqr, q3 + k*iqr
    return s[(s < lo) | (s > hi)]

for r, g in df.groupby('地域'):
    out = detect_outliers(g['L3221'])
    if len(out):
        print(f'{r}: 外れ値 {len(out)} 件')
        print(g.loc[out.index, ['Prefecture', 'L3221']].to_string(index=False))

📤 実行結果(2023 年度の実測値)

中部: 外れ値 1 件 Prefecture L3221 福井県 276690 四国: 外れ値 1 件 Prefecture L3221 愛媛県 223423

💬 ブロック内の 1.5×IQR ルールでは、 中部の福井(276,690 円)と四国の愛媛(223,423 円)がいずれも下側の外れ値として検出される。

🧮 実値で計算してみる — SSDSE-B-2026

箱ひげ図の Q1・中央値・Q3・IQR・上下ひげ閾値を、 SSDSE-B-2026 の A1101(人口、 2023 年)で実際に計算します。 47 都道府県の値を昇順に並べ、 四分位を取り、 IQR × 1.5 で外れ値閾値を求めるという一連の手順を電卓的に追体験。 Python の numpy.percentile と完全一致するか確認しましょう。

SSDSE-B-2026 (2023) の A1101 で実値計算:Q1=1,034,000、 中央 1,549,000、 Q3=2,636,500、 IQR=1,602,500、 上ヒゲ閾値=2,636,500 + 1.5×1,602,500 = 5,040,250。 これを超える 9 都道府県(東京 14,086,000/神奈川 9,229,000/大阪 8,763,000/愛知 7,477,000/埼玉 7,331,000/千葉 6,257,000/兵庫 5,370,000/福岡 5,103,000/北海道 5,092,000)が点として描画される。

都道府県A1101 総人口A1303 65 歳以上L3221 消費支出
東京都14,086,0003,205,000341,320
神奈川県9,229,0002,390,000306,565
大阪府8,763,0002,424,000271,246
愛知県7,477,0001,923,000300,221
埼玉県7,331,0002,012,000344,092
千葉県6,257,0001,756,000306,943

上記は SSDSE-B-2026 (2023) からの抜粋。 手計算で確認した値が、 後述の Python 実装で得る値と一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。

🧮 数式に値を入れて手で計算する

合成データ x = [3, 5, 8, 2, 7, 12, 4] (n=7、 多様な整数) を使って Q1・中央値・Q3・IQR・外れ値判定 (1.5×IQR ルール) を Step 1〜4 で展開し、 Python (numpy) で再現する。 手計算と Python の結果が一致することを確認する。

📐 用語の主要数式 (再掲)

$$ \mathrm{IQR} = Q_3 - Q_1, \quad \text{外れ値} = \big\{\, x : x < Q_1 - 1.5\,\mathrm{IQR} \;\text{or}\; x > Q_3 + 1.5\,\mathrm{IQR} \,\big\} $$

Step 1: データ準備 (合成、 多様な整数 n=7)

サンプル値 x
x₁3
x₂5
x₃8
x₄2
x₅7
x₆12
x₇4

Step 2: 昇順ソート + 位置インデックスの算出

項目計算結果
ソート後[2, 3, 4, 5, 7, 8, 12]
中央値 Median の位置(n+1)/2 = (7+1)/24 番目 = 5
Q1 の位置 (線形補間)0.25 × (n−1) = 0.25 × 6 = 1.5 → index 1 と 2 の中点3 + (4−3)×0.5 = 3.5
Q3 の位置 (線形補間)0.75 × (n−1) = 0.75 × 6 = 4.5 → index 4 と 5 の中点7 + (8−7)×0.5 = 7.5

numpy.percentile デフォルト (linear) と同じ補間方式。

Step 3: IQR と外れ値判定境界

項目計算結果
IQRQ3 − Q1 = 7.5 − 3.54.0
下限 (lower fence)Q1 − 1.5 × IQR = 3.5 − 1.5×4.0 = 3.5 − 6.0−2.5
上限 (upper fence)Q3 + 1.5 × IQR = 7.5 + 1.5×4.0 = 7.5 + 6.013.5

Step 4: 外れ値の判定

判定 (−2.5 ≤ x ≤ 13.5)外れ値?
2−2.5 ≤ 2 ≤ 13.5×
3−2.5 ≤ 3 ≤ 13.5×
4−2.5 ≤ 4 ≤ 13.5×
5−2.5 ≤ 5 ≤ 13.5×
7−2.5 ≤ 7 ≤ 13.5×
8−2.5 ≤ 8 ≤ 13.5×
12−2.5 ≤ 12 ≤ 13.5×

最大値 12 は上限 13.5 を超えないため、 1.5×IQR ルール上は外れ値ではない (上ヒゲがそこに伸びる)。

🐍 同じ計算を Python で再現

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np

x = np.array([3, 5, 8, 2, 7, 12, 4])

q1     = np.percentile(x, 25)
median = np.percentile(x, 50)
q3     = np.percentile(x, 75)
iqr    = q3 - q1
lower  = q1 - 1.5 * iqr
upper  = q3 + 1.5 * iqr

outliers = x[(x < lower) | (x > upper)]

print(f"ソート後 = {np.sort(x)}")
print(f"Q1     = {q1}")
print(f"Median = {median}")
print(f"Q3     = {q3}")
print(f"IQR    = {iqr}")
print(f"下限   = {lower}")
print(f"上限   = {upper}")
print(f"外れ値 = {outliers.tolist()}")

📤 実行結果

ソート後 = [ 2 3 4 5 7 8 12] Q1 = 3.5 Median = 5.0 Q3 = 7.5 IQR = 4.0 下限 = -2.5 上限 = 13.5 外れ値 = []

💬 手計算 (Step 2: Q1=3.5, Median=5, Q3=7.5 / Step 3: IQR=4, 下限=−2.5, 上限=13.5 / Step 4: 外れ値なし) と Python 出力が完全一致。 numpy.percentile デフォルトの線形補間で再現できる。

🐍 Python での箱ひげ図描画

matplotlib

🎯 解説: seaborn.boxplot でカテゴリ別並列表示。 SSDSE-B-2026 を地方ブロック別に分けて高齢化率の分布を比較。 グループ間差の可視化に最適。
📥 入力例: data/raw/SSDSE-B-2026.csv x='地域', y='高齢化率' カテゴリ: 8 ブロック
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import matplotlib.pyplot as plt
import inspect

# 1変数
plt.boxplot(df['食料費'])

# 複数変数
# 目盛りのラベルは boxplot の引数名が版によって違う(labels → tick_labels)。
# plt.xticks で後から付ければ、どの版でも同じように動く。
_cols = ['食料費', '住居費', '教育費']
plt.boxplot([df[c] for c in _cols])
plt.xticks(range(1, len(_cols) + 1), _cols)

# 横向き(matplotlib 3.10 で vert=False は orientation='horizontal' に変わった)
_p = inspect.signature(plt.boxplot).parameters
_horiz = {'orientation': 'horizontal'} if 'orientation' in _p else {'vert': False}
plt.boxplot(df['食料費'], **_horiz)

# notched + patch_artist で色付け
bp = plt.boxplot(data, notch=True, patch_artist=True)
for patch in bp['boxes']:
    patch.set_facecolor('lightblue')
📤 実行例(実測) このブロックは標準出力を出さない(図を描く・変数を定義するだけ)。
💬 読み方: 中央値の高さ・箱の長さ(ばらつき)・外れ値の有無でグループを比較。 箱が重なれば差は小さい、 離れれば大きい。 後続で t 検定や ANOVA を併用。

seaborn(推奨)

🎯 解説: 箱ひげ図 + 個別点(ストリッププロット)で「分布形状 + データ点」を同時表示。 sns.boxplot + sns.stripplot の組み合わせで詳細表現。
📥 入力例: data/raw/SSDSE-B-2026.csv x='地域', y='高齢化率' alpha=0.5(点の透明度)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import seaborn as sns

# 1変数
sns.boxplot(y=df['食料費'])

# 群比較
sns.boxplot(x='地域', y='食料費', data=df)

# 入れ子の比較
sns.boxplot(x='地域', y='食料費', hue='都市規模', data=df)

# 個別点と重ねる
sns.boxplot(x='地域', y='食料費', data=df)
sns.stripplot(x='地域', y='食料費', data=df, color='black', alpha=0.5)
📤 実行例: 箱の上に各県の点が散らばる 小サンプル(北海道=1)で点が 1 つだけ 関東は 7 点バラバラ
💬 読み方: 箱ひげ図単体ではサンプル数が見えない。 個別点と組み合わせると n の大小・点の分布が明確。 n<10 では箱ひげより個別点表示の方が誠実。

pandas(直接)

df.boxplot(column=['食料費', '住居費', '教育費']) # 群別 df.boxplot(column='食料費', by='地域')

plotly(インタラクティブ)

🎯 解説: バイオリンプロット(箱ひげ図の拡張)で確率密度を含めた分布形状を可視化。 双峰性や歪みが直感的に見える。
📥 入力例: data/raw/SSDSE-B-2026.csv sns.violinplot(x='地域', y='高齢化率') 内部に箱ひげ図
1
2
3
4
5
6
import plotly.express as px

fig = px.box(df, x='地域', y='食料費',
             points='all',           # 個別点も表示
             hover_data=['Prefecture'])
fig.show()
📤 実行例: バイオリン形の幅 = 密度 四国は単峰 関東は下に長い裾(東京 22.8% だけ大きく低い)
💬 読み方: 幅が広い部分はその値付近にデータが多い。 双峰なら 2 つのグループが混在。 inner='box' で箱ひげ図を内蔵、 inner='quartile' で四分位線。

🤖 機械学習での箱ひげ図

① 特徴量の分布チェック

機械学習の前処理で、 全特徴量の分布を箱ひげ図で確認。 「異常な値」「単位の違い」「スケーリング必要性」を判定。

② クロスバリデーションスコアの可視化

複数モデルの CV スコアを箱ひげ図で比較:

🎯 解説: notched boxplot(切り欠き付き)で中央値の 95% 信頼区間を表示。 切り欠きが重ならなければ中央値に統計的差。 簡易検定として利用。
📥 入力例: data/raw/SSDSE-B-2026.csv plt.boxplot(data, notch=True) カテゴリ: 関東 vs 東北(高齢化率)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score

# ── この抜粋だけで動くように、分類用の X, y を作る ──
_d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
_d = _d[_d['SSDSE-B-2026'] == 2023]
X = StandardScaler().fit_transform(
    _d[['A4101', 'A1303', 'L3221']].astype(float))     # 出生数・65歳以上人口・消費支出
y = (_d['A1101'] > _d['A1101'].median()).astype(int)   # 総人口が中央値より多いか

# XGBoost はブラウザ版 Python に入っていないので、
# 同じ勾配ブースティング系の sklearn GradientBoostingClassifier で代用する
scores_lr = cross_val_score(LogisticRegression(max_iter=1000), X, y, cv=10)
scores_rf = cross_val_score(RandomForestClassifier(random_state=0), X, y, cv=10)
scores_gb = cross_val_score(GradientBoostingClassifier(random_state=0), X, y, cv=10)

sns.boxplot(data=[scores_lr, scores_rf, scores_gb])
plt.xticks([0, 1, 2], ['LR', 'RF', 'GBDT'])
plt.ylabel('CV Score')
for name, s in [('LR', scores_lr), ('RF', scores_rf), ('GBDT', scores_gb)]:
    print(f'{name}: 平均 {s.mean():.3f}{s.std():.3f})')
📤 実行例: 箱の中央に「切れ込み」(2023 年度の実測) 関東の notch: 25.8〜30.3(中央値 28.1、 n=7) 東北の notch: 34.1〜36.1(中央値 35.1、 n=6) 重ならない → 有意差の目安
💬 読み方: 切り欠きは中央値 ± 1.57·IQR/√n の範囲。 重ならない → α=0.05 で中央値差あり。 ただし正確な検定としては Wilcoxon 順位和検定推奨。

③ ハイパーパラメータの影響

同じハイパーパラメータで複数回学習し、 結果を箱ひげ図に。 安定性が分かる。

④ 外れ値検出の標準ツール

IQR ベースの自動外れ値検出は、 最もシンプルで実用的な手法の1つ。

🚧 箱ひげ図の落とし穴

1️⃣ 多峰性が見えない

箱ひげ図は5数要約しか見せないため、 二峰性(2つのピーク)は見えません。 バイオリンプロットや ヒストグラムを併用。

2️⃣ サンプルサイズが見えない

n=10 のデータと n=10000 のデータが同じ箱ひげ図になりえます。 サンプル数を別途記載しましょう(例:軸ラベルに n=○)。

3️⃣ 外れ値判定はあくまで「候補」

ひげの外側 = 外れ値ではない。 「外れ値の可能性がある点」程度の意味。 ドメイン知識で確認を。

4️⃣ ひげの定義をライブラリで確認

matplotlib のデフォルトは Tukey ルールだが、 他ソフトでは違うことも。 説明文に「Tukey の 1.5×IQR ルール」と明記。

5️⃣ 縦軸のスケール

裾の長い分布だと、 外れ値が多すぎて箱が潰れて見えなくなる。 log スケールで描くのも検討。

📜 箱ひげ図の歴史

50年以上の歴史を持ち、 EDA・統計報告・機械学習の標準ツールとして使われ続けています。

🐍 Python 実装バリエーション — matplotlib / seaborn / plotly / scipy

1. matplotlib — 細かい制御が効く標準実装

🎯 解説: 外れ値検出専用の箱ひげ図。 SSDSE-B-2026 で IQR×1.5 ルールに該当する県を抽出。 データクリーニング前の必須チェック。
📥 入力例: data/raw/SSDSE-B-2026.csv Q1, Q3 = quantile(0.25, 0.75) IQR = Q3-Q1 外れ値: x < Q1-1.5×IQR または x > Q3+1.5×IQR
📤 実行例: 高齢化率の外れ値(2023 年度、 47 都道府県)
  下側: 東京(22.8%)・沖縄(23.8%)
  上側: なし(最大の秋田 39.1% は上限 40.0% の内側)
  2 都県のみ

💬 読み方: 外れ値は誤データかも実態かを判断。 SSDSE 公的データでは実態。 z-score>3 と IQR ルールで結果が異なる場合は分布形状を確認。 削除でなく log 変換も検討。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import pandas as pd
import matplotlib.pyplot as plt

# ── この抜粋だけで動くように、地方ブロック別の消費支出をまとめる ──
_d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
_d = _d[_d['SSDSE-B-2026'] == 2023].copy()
def _blk(code):
    n = int(str(code)[1:3])
    return ('北海道' if n == 1 else '東北' if n <= 7 else '関東' if n <= 14
            else '中部' if n <= 23 else '近畿' if n <= 30 else '中国' if n <= 35
            else '四国' if n <= 39 else '九州沖縄')
_d['地域'] = _d['Code'].map(_blk)
_g = _d.groupby('地域')['L3221']
labels = list(_g.groups.keys())
data = [_g.get_group(k).values for k in labels]

fig, ax = plt.subplots(figsize=(9, 5))
# ラベルは set_xticklabels で付ける
# (boxplot の引数名は matplotlib の版で labels / tick_labels と違うため)
bp = ax.boxplot(data, notch=True, patch_artist=True,
                whis=1.5, showmeans=True,
                medianprops=dict(color='black', lw=2),
                meanprops=dict(marker='D', mfc='red', mec='black'))
ax.set_xticks(range(1, len(labels) + 1))
ax.set_xticklabels(labels, rotation=20)
for patch, color in zip(bp['boxes'], plt.cm.Set2(range(len(data)))):
    patch.set_facecolor(color)
ax.set_ylabel('消費支出(二人以上の世帯、円/月)')
ax.grid(alpha=.3, axis='y')
plt.tight_layout()
plt.show()

2. seaborn — 美しい統計可視化

🎯 解説: pandas.DataFrame.boxplot でデータフレームから直接箱ひげ図を作成。 by 引数でグループ別に並列表示が可能な簡便な方法。
📥 入力例: data/raw/SSDSE-B-2026.csv df.boxplot(column='高齢化率', by='地域')
📤 実行例: 8 ブロック × 箱ひげ図
  自動グルーピング
  タイトル: 'Boxplot grouped by 地域'

💬 読み方: pandas の boxplot は seaborn より見た目が地味だが手軽。 column を複数指定すれば複数変数を並列表示。 細かいカスタマイズは matplotlib/seaborn 推奨。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import seaborn as sns
fig, axes = plt.subplots(1, 3, figsize=(15, 4.5))
sns.boxplot(data=df, x='地域', y='L3221', ax=axes[0])
sns.violinplot(data=df, x='地域', y='L3221', inner='quartile', ax=axes[1])
sns.boxplot(data=df, x='地域', y='L3221', ax=axes[2])
sns.stripplot(data=df, x='地域', y='L3221', color='black',
              size=4, alpha=.6, ax=axes[2])
for ax in axes:
    ax.tick_params(axis='x', rotation=30)
plt.tight_layout()

3. plotly express — インタラクティブ箱ひげ

🎯 解説: plotly の px.box で地方ブロック別の消費支出 L3221 を箱ひげ図にする。 points='all' で個々の県も点で重ね、 hover_data で県名を出せるようにしている。
📥 入力例: data/raw/SSDSE-B-2026.csv df['高齢化率'].describe() count, mean, std, min, 25%, 50%, 75%, max
📤 実行例: count=47, mean=31.6, std=3.3(高齢化率、 2023 年度)
  min=22.8, 25%=30.1, 50%=31.8, 75%=34.0, max=39.1
  歪度=-0.6(左に裾を引く)

💬 読み方: 中央値(31.8)と平均(31.6)は近いが、 歪度 -0.6 で低い側に裾。 SD=3.3 に対し IQR/1.35 ≈ 2.9 → SD の方が大きく、 東京・沖縄の低値側の裾が SD を押し上げている。 ズレが大きいと歪み大。

1
2
3
4
5
import plotly.express as px
fig = px.box(df, x='地域', y='L3221', points='all',
             hover_data=['Prefecture'])
fig.update_layout(title='地方ブロック別 消費支出の分布')
fig.write_html('boxplot.html')

4. scipy.stats と numpy で五数要約を自前計算

🎯 解説: 箱ひげ図が表している 5 数要約(最小・Q1・中央値・Q3・最大)と IQR・平均・標準偏差を、 numpy と scipy.stats.iqr で数値としても出す。 図と数値を突き合わせて読む。
📥 入力例: data/raw/SSDSE-B-2026.csv y = df['A1101'](人口) plt.yscale('log')
📤 実行例: 対数軸の箱(2023 年度)
  中央値≈155万、 鳥取≈54万、 東京≈1409万
  オーダー差が見やすい

💬 読み方: スケール差 30 倍超なら対数軸が有効。 線形軸では東京の外れ値が支配的で他県が潰れる。 対数軸では全県が比較しやすい。 軸ラベルに「(log scale)」明記。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from scipy import stats
import numpy as np
x = df['L3221'].values
print('min   :', np.min(x))
print('Q1    :', np.percentile(x, 25))
print('Median:', np.median(x))
print('Q3    :', np.percentile(x, 75))
print('max   :', np.max(x))
print('IQR   :', stats.iqr(x))
print('mean  :', np.mean(x))
print('SD    :', np.std(x, ddof=1))

5. ノッチ付き箱ひげで中央値の 95%CI を可視化

ノッチ幅は中央値 ± 1.57·IQR/√n。 ノッチが重ならない群対は中央値が有意に異なる目安(簡易検定)。

🎯 解説: ソート済み箱ひげ図でカテゴリを中央値順に並べ替え。 SSDSE-B-2026 の地方ブロックを高齢化率の中央値で降順表示。
📥 入力例: data/raw/SSDSE-B-2026.csv order = df.groupby('地域')['高齢化率'].median().sort_values().index
📤 実行例: 左から低い順(2023 年度の中央値)
  関東(28.1)→ 近畿(30.0)→ 中部(31.6)→ … → 四国(34.8)→ 東北(35.1)
  順位が一目瞭然

💬 読み方: アルファベット順より中央値順の方が比較しやすい。 トレンドが見える。 順位の解釈には注意(中央値 vs 平均値 vs 最大値)。

1
2
3
4
5
6
7
8
9
fig, ax = plt.subplots(figsize=(9, 5))
# ラベルは set_xticklabels で付ける(版によって labels / tick_labels と違うため)
ax.boxplot(data, notch=True, bootstrap=10000,
           showmeans=True, whis=1.5)
ax.set_xticks(range(1, len(labels) + 1))
ax.set_xticklabels(labels, rotation=20)
ax.set_title('ノッチ付き箱ひげ(中央値95%CI)')
plt.tight_layout()
plt.show()

🐍 Python 実装 — 箱ひげ図

公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで 箱ひげ図 を動作させます。 まずはこのまま実行してみてください。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) L3221(消費支出(二人以上の世帯)) 北海道 5,092,000 1,681,000 296,888 東京都 14,086,000 3,205,000 341,320 沖縄県 1,468,000 350,000 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 箱ひげ図 を SSDSE-B-2026 で実行する最小コード
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023 年のみ抽出
print(df.shape)  # (47, 112)
print(df[['Prefecture','A1101','A1303','L3221']].head())

import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(7,5))
# matplotlib 3.9 以降、boxplot の labels= は tick_labels= に改名された
ax.boxplot(df['A1101'], tick_labels=['A1101 総人口'])
ax.set_title('SSDSE-B-2026 (2023): A1101 箱ひげ図')
q1, q3 = df['A1101'].quantile([0.25,0.75])
iqr = q3 - q1
print(f'Q1={q1:.0f} Q3={q3:.0f} IQR={iqr:.0f}')
print(f'上ヒゲ閾値={q3 + 1.5*iqr:.0f}')
plt.savefig('boxplot_demo.png', dpi=100)
📤 実行例(実測) (47, 112) Prefecture A1101 A1303 L3221 0 北海道 5092000 1681000 296888 12 青森県 1184000 417000 263371 24 岩手県 1163000 407000 298536 36 宮城県 2264000 662000 305541 48 秋田県 914000 357000 272086 Q1=1034000 Q3=2636500 IQR=1602500 上ヒゲ閾値=5040250

▶ 実行 を押せばこのページの中でそのまま動きます(ライブラリもデータも同梱済みで、 準備は要りません)。 手元の Python に移して動かすときは pip install matplotlib numpy pandas scikit-learn scipy seaborn が必要です。 読んでいるデータは data/raw/SSDSE-B-2026.csv。 日本語を含むので encoding='cp932' の指定を落とさないでください。

⚠️ 箱ひげ図の落とし穴 — 6 つの典型ミス

① 多峰性(バイモーダル)が完全に隠れる

箱ひげ図は分布を「四分位 + ひげ」に要約するため、 二山分布(例:男女混合の身長)でも単一の箱に潰される。 ヒストグラム・カーネル密度プロット・バイオリン図と併用しないと、 重要なクラスター構造を見逃す。 教育の現場でこれが原因で「平均だけ見て大丈夫」と誤判断する事例は後を絶たない。 とくに介入効果のサブグループ差を箱ひげのみで判断するのは危険。 必ず生データの散布も重ねる(strip plot / swarm plot 併用)。

② サンプル数の差を可視化しない

「北海道(n=1)」と「関東(n=7)」と「九州沖縄(n=8)」の箱ひげを並べると、 サンプル数の極端な差が分からない。 北海道は箱の代わりに 1 点しかプロットできず、 ノイズで判断する事故が起きる。 群ごとの n を箱の下に明記する、 violinplot で帯幅にサンプル数を反映する、 ノッチ付き箱ひげで中央値の信頼区間を示す、 のいずれかで補強する。 n が極端に小さい群は注釈で警告するのが親切。

③ ひげの定義が実装で違うことを知らない

matplotlib の whis 既定値は 1.5、 R の base boxplot も 1.5、 seaborn は matplotlib をラップ。 一方で「ひげ=最小・最大」と単純定義する実装(Excel 既定や Tableau の一部)もある。 同じデータでも見た目が変わるので、 図の凡例に「whisker = Q1 - 1.5·IQR / Q3 + 1.5·IQR」と明記。 論文ではしばしばこの定義の違いから「外れ値が違う」と査読で指摘される。 自分の使うライブラリの既定値を必ず確認すること。

④ 外れ値を機械的に削除する

IQR ルールで「自動的に外れ値」と判定されても、 それは「分布の裾」を意味するだけで「異常値」とは限らない。 都道府県データで東京・大阪が外れるのは経済構造上当然であり、 削除すると本質情報が失われる。 外れ値はまず原因を調べ、 (a) 計測ミスなら修正、 (b) 異常な状況の真値なら残す、 (c) 主分析と感度分析を並走、 という流れが正道。 「箱ひげで外れ値と表示された=削除」と短絡しない。

⑤ 群間比較に t 検定を「箱ひげが重なってないから有意」で判断する

箱ひげ図の箱(IQR)の重なり具合は統計検定の有無とは別物。 IQR が重なっていても t 検定で有意になることはあるし、 逆も真。 群間比較を主張するならノッチ付き箱ひげ(ノッチが重ならない=中央値が有意に異なる目安)か、 t 検定 / Mann-Whitney U の p 値を併記する。 「箱が分離して見えた」だけで結論しない。

⑥ Y 軸を切り詰めて見かけの差を誇張する

箱ひげ図でも Y 軸の範囲操作は強力な「嘘の図」を生む。 たとえば「平均給与の地方差」を見せたいときに Y 軸を 250-350 に切ると群間差が大きく見え、 0-400 にすると小さく見える。 学術論文では原則「ゼロ起点」もしくは「データ範囲全体」を表示すべきだが、 商業プレゼンでは恣意的な切り詰めが横行する。 自分が作る側でも見る側でも、 Y 軸スケールを常にチェックする習慣をつける。

⚠️ よくある落とし穴 — 箱ひげ図

箱ひげ図で初学者が陥る典型ミス。 外れ値点を即「異常」と判定する、 群間比較で標本サイズの違いを無視する、 など。 SSDSE-B-2026 で 47 都道府県を可視化すると東京・大阪が必ず外れ値マークになり、 「除外すべきか保持すべきか」の判断が頻出します。

❌ 外れ値=異常 と誤読
Tukey 規則 1.5×IQR は規約であって異常判定ではない。 ドメイン知識で再評価する。
❌ 分布形状が見えない
二峰性・歪み・尖度などは箱ひげに現れない。 ヒストグラム・KDE と併用する。
❌ n が小さいと不安定
n<10 では Q1/Q3 自体の誤差が大きい。 個別データ点(stripplot)を重ねる。
🛡 箱ひげ図 読解の三原則:「外れ値点 = 異常 と即断しない(分布の裾を示す可能性)」「群間比較ではノッチや 95% CI を併記する」「Y 軸スケールが 0 から始まっているか必ず確認する」。 SSDSE-B-2026 で東京を含むデータを描いたら、 対数軸表示も併用するのが鉄則です。

🗺️ 概念マップ — 3つの視点で体系を理解する

箱ひげ図 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。

📍 体系階層のパス

🌐 統計・データサイエンス記述統計可視化箱ひげ図

① 🔗 関係マップ — 「他の手法とどう繋がっているか」

中心に 箱ひげ図 を置き、 そこから ヒストグラム・散布図・中央値・四分位・外れ値検出・正規分布 など 計 9 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語あとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。

凡例:現在の用語上位カテゴリ兄弟(並列)前提発展形応用先2階層先

② ⭕ 包含マップ — 「どのカテゴリに含まれているか」

大きな円が小さな円を包含する Circle Packing 図。 「箱ひげ図」は緑色でハイライト

📍現在地:統計・データサイエンス

③ 🌳 ツリーマップ — 「面積で見るボリューム比較」

長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「箱ひげ図」は緑色でハイライト

🎯 3つのマップの使い分け

マップ 分かること こんな時に見る
🔗 関係マップ手法間の横の関係(前提→発展→応用)「次に何を学べばよい?」 学習順序の判断
⭕ 包含マップ分類体系の入れ子構造(上位⊃下位)「この手法はどんなジャンルに属する?」
🌳 ツリーマップ分野の規模比較(面積=ボリューム)「データサイエンス全体の俯瞰像」

💡 3 つの図は同じ位置関係を別の角度から描いています。 関係マップは 箱ひげ図隣に何が並ぶかを、 包含マップとツリーマップは 統計・データサイエンス可視化 → 箱ひげ図 という入れ子の位置を示します。 「可視化には他に何があったか」を思い出したいときは包含マップを、 「次に何を読むか」を決めたいときは関係マップを開いてください。

🔗 隣接手法への橋渡し

箱ひげ図 (Tukey 1977) は 5 数要約 (最小・Q1・中央・Q3・最大) と外れ値を一目で示すロバスト可視化で、 群間比較と分布形の把握を兼ねる。

SSDSE-B-2026 で 47 都道府県の高齢化率を 8 地方区分で箱ひげ図化すると、 中央値の地方差・四国/東北の高さ・関東の幅広さ・沖縄の外れ値が一画面で読める。

🌳 手法選択フロー

「boxplot」を含む手法選択は、 データの性質・分析目的・運用制約の 3 軸で決まる。 以下に典型シナリオと対応する手法の組合せを示す。

典型シナリオ別の手法選択

シナリオ重視する観点候補手法
データが大規模 (n が多い、 高次元)計算コスト / メモリ / 並列化が必要標準手法
外れ値が多い / ノイズあり頑健性 / 外れ値除去 / 中央値ベース高速版
解釈性を重視する線形 / 木構造 / ルールベース頑健版
予測精度を最優先するアンサンブル / ハイパラ調整 / 交差検証高次元対応版
データが少ない正則化 / ベイズ / 転移学習 / 簡素なモデル小データ向け
リアルタイム/オンライン処理ストリーミング / 軽量モデル / 逐次更新解釈重視版

選んだ後の検証ステップ

  1. 前処理確認: 標準化 / 欠損処理 / カテゴリ変数のエンコード が適切か
  2. ハイパラ調整: 交差検証で安定する値を選ぶ
  3. 性能評価: タスクに応じた指標 (RMSE / Accuracy / F1 / AUC / シルエット等) を複数併用
  4. 頑健性チェック: 別手法 / 別シードで結果が大きく違わないか確認
  5. 解釈: 結果を分野知識と照らし合わせ、 意味のある発見になっているか検討

👁️ 直感をもう一段深める — 五数要約が「分布の骨格」になる理由

箱ひげ図は、 データの全点を捨ててたった 5 つの位置(最小・第 1 四分位 Q1中央値・第 3 四分位 Q3・最大)だけを残す「究極の要約」です。 なぜこの 5 点で十分なのか。 それは分布を語るとき私たちが本当に知りたいのが 中心はどこか(中央値)・どれだけ散らばるか(IQR)・左右どちらに歪んでいるか(中央値の箱内の偏り)・裾はどこまで伸びるか(ひげ)・例外はあるか(点) の 5 つに集約されるからです。

SSDSE-B-2026(2023 年・47 都道府県)の食料費で「読み方」を体感

実データ L322101(食料費・二人以上の世帯・千円/月)の五数要約は次の通りです(numpy.percentile の線形補間・実測値)。

値(千円/月)図のどこに現れるか
最小71.1下ひげの先端
Q1(下から 25%)76.0箱の下辺
中央値(50%)79.9箱の中の太線
Q3(下から 75%)85.1箱の上辺
最大97.8上ひげの先端
IQR = Q3 − Q19.1箱の高さ(中央 50% の幅)
上側フェンス Q3+1.5·IQR98.7外れ値判定の上限

最大 97.8 千円は上側フェンス 98.7 千円の内側なので、 食料費に 外れ値は 1 件も出ません。 一方、 平均 80.6 千円は中央値 79.9 千円よりわずかに大きく、 これは分布がごく弱く右に歪んでいる(高い側に長い裾がある)ことを示します。 中央値と平均の差 +0.7 千円という小さなズレを箱ひげ図(◆平均マーカー付き)で読み取れるのが、 この図の解像度です。

💡 中心・広がり・歪みを一目で:箱の位置=中心、 箱の高さ(IQR)=広がり、 中央値線が箱の中で上下どちらに寄っているか=歪み。 食料費では中央値 79.9 が箱 [76.0, 85.1] のやや下寄り(下半分 3.9・上半分 5.2)で、 右裾がわずかに長い分布だと箱の形だけで判断できます。 これが「五数要約 = 分布の骨格」の意味です。

⚠️ 落とし穴をさらに深掘り — 箱ひげ図が「隠す」もの

箱ひげ図は強力ですが、 要約である以上、 必ず何かを捨てています。 上の「⚠️ 6 つの典型ミス」を踏まえ、 実務で誤読につながりやすい論点を実データとともにもう一段掘り下げます。

1️⃣ 多峰性・分布の形状は原理的に見えない

五数要約は分位点の位置しか持たないため、 山が 2 つある分布(二峰性)と 1 つの分布が同じ箱ひげ図になりえます。 例えば都市部と地方が混在した変数は「中央に谷、 両端に山」という形をとりがちですが、 箱ひげ図ではただの 1 つの箱に潰れます。 分布の形そのものを見たいときは、 同じデータを ヒストグラム や KDE(カーネル密度推定)で必ず併用します。 バイオリンプロット(箱ひげ + KDE)は、 まさにこの弱点を補うために設計された派生図です。

2️⃣ 外れ値の定義(1.5×IQR)は「規約」であって真理ではない

Tukey の係数 1.5 は「正規分布ならデータの約 99.3%(±2.7σ 相当)が内側に入る」という経験則から選ばれた任意の定数です。 3.0 にすれば「far out(極端な外れ値)」の判定になります。 同じ SSDSE の消費支出 L3221(実測)で係数を変えると判定は次のように変わります。

係数 k下側フェンス Q1−k·IQR外れ値として点になる県
1.5(標準)237.5 千円愛媛県(223.4)の 1 件
3.0(far out)195.5 千円なし(愛媛も内側)

Q1=279.5・Q3=307.5・IQR=28.0(いずれも実測)。 「愛媛が外れ値かどうか」は係数の選び方だけで反転します。 外れ値の点は「異常」ではなく「Tukey 規約上フェンスの外に出た候補」に過ぎません。 外れ値外れ値処理のページも参照し、 削除ではなく対数変換や頑健統計量での対応も検討します。

3️⃣ 標本サイズが図に一切反映されない

n=5 の箱ひげ図と n=5000 の箱ひげ図は見た目で区別できません。 この教材の SSDSE 地方ブロック比較でも、 北海道は n=1、 中国は n=5、 中部は n=9 と大きく違うのに、 箱の見た目は「信頼度の差」を語りません。 n が小さいと Q1・Q3 自体の推定誤差が大きく、 箱の位置がたまたまである可能性が高まります。 対策は (a) 軸ラベルに n を明記、 (b) ストリップ/スウォームで個別点を重ねる、 (c) ノッチ(後述)で中央値の信頼区間を示す、 の 3 つです。

4️⃣ ひげの定義は「流儀」で異なる

「ひげの先端」が何を指すかは実装で割れます。 (a) Tukey 型=フェンス内の実データの最小・最大(matplotlib・seaborn の既定)、 (b) min-max 型=外れ値を出さず単純に最小値・最大値まで伸ばす、 (c) パーセンタイル型=2–98% 点や 9–91% 点まで、 (d) 標準偏差型=平均 ± n·SD。 同じデータでも図の印象が変わるため、 図には必ず「Tukey の 1.5×IQR ルール」などひげの定義を明記します。

5️⃣ 中央値だけを見て平均を見落とす

箱ひげ図の主役は中央値で、 既定では平均を描きません。 しかし歪んだ分布では平均と中央値が乖離します。 実測の消費支出 L3221 では 平均 295.9 千円 < 中央値 300.7 千円(差 −4.8 千円)で、 左に長い裾(低い側の県)が平均を押し下げています。 showmeans=True で◆平均を重ねると、 平均中央値のズレ=歪みを一目で確認できます。

6️⃣ 群比較での誤解 — 「箱が重なる=差がない」ではない

複数群を横に並べたとき、 箱の重なり具合を直感的な有意差判定に使いがちですが、 これは正確ではありません。 中央値の差の検定は箱の重なりではなく、 ノッチ(中央値の信頼区間)や t 検定ANOVA・Kruskal-Wallis 検定で行うべきです。 また群ごとに n が違えば、 同じ箱幅でも情報量が違います。

🚀 発展 — 箱ひげ図から広がる可視化と統計の世界

箱ひげ図の弱点(形状・標本数・信頼区間が見えない)を補う派生図と、 五数要約の背後にある統計理論を整理します。 それぞれ「何を追加で見せるか」を一言で言えるようにしておくと、 場面に応じて選べます。

① 形状を足す派生図 — バイオリン / スウォーム / ビースワーム

箱ひげに何を足すか向いている場面
バイオリンプロット左右対称の KDE(密度)で分布の形多峰性・歪みを見せたい・中〜大規模
スウォーム(swarm)個別点を重ならないよう横に配置n が小さく全点を見せたい
ビースワーム(beeswarm)密度の高い所で点を横に広げる小〜中規模・点の密集を可視化
ストリップ(strip)個別点をランダムジッタで配置手軽に全点を重ねたい
レターバリュー(boxen)多段のパーセンタイルを階段状に大量データで裾を詳しく

(本サイトにバイオリン図・スウォーム図の個別ページはまだ無いため、 ここではテキストで整理しています。 実装例はこのページの「🐍 Python」節の sns.violinplot / sns.swarmplot を参照。)

② ノッチ付き箱ひげ図 — 中央値の信頼区間を「くびれ」で示す

ノッチ(notch)は中央値の約 95% 信頼区間を箱のくびれとして描きます。 区間は 中央値 ± 1.57 · IQR / √n で近似されます。 実測の食料費(n=47・IQR=9.1・中央値 79.9)で計算すると、

$$ 79.9 \pm 1.57 \cdot \frac{9.1}{\sqrt{47}} = 79.9 \pm 2.08 = [77.9,\; 82.0] $$

2 群のノッチ(くびれ)が重ならなければ、 中央値に α=0.05 水準の差があると目安として読めます。 式に √n が入るため、 n が大きいほどくびれは細く(信頼区間が狭く)なり、 標本サイズの効果を図に取り込めます(落とし穴 3 の部分的な対策)。 ただし正確な検定としては Wilcoxon 順位和検定などを併用します。

③ 五数要約と Tukey の EDA

箱ひげ図は John Tukey が 1977 年の『探索的データ解析(EDA)』で体系化しました。 五数要約は分布に正規性などの仮定を置かず、 順序統計量(並べ替えた順位)だけで中心と散らばりを表す点が本質です。 このため 正規分布から大きく外れた実データや、 各種分布が混ざったデータでも安定して機能します。 平均標準偏差分散が 1 点の極値で大きく動くのに対し、 中央値・IQR は頑健です。

④ 外れ値検出との関係

箱ひげ図のフェンス(Q1−1.5·IQR / Q3+1.5·IQR)は、 そのまま IQR 法による外れ値検出アルゴリズムです。 z-score 法(平均 ± 3σ)が平均・標準偏差を使うため外れ値自身に引っ張られやすいのに対し、 IQR 法は中央値・四分位ベースで頑健です。 詳細は 外れ値外れ値処理を参照。 実測の総人口 A1101 では上側フェンス 504 万人を超える 9 都道府県(東京 1409 万など)が点になりますが、 これは「異常」ではなく大都市圏という分布の右裾そのものです。

⑤ 群間比較への発展

箱ひげ図の真価は群を横に並べる比較にあります。 中央値の高さ・箱の重なり・裾の長さ・外れ値の偏りを一望でき、 平均だけでは見えない「ばらつきの差」を捉えられます。 比較で差の有無を定量化したくなったら、 2 群なら t 検定(正規性が疑わしければ Mann-Whitney U)、 3 群以上なら ANOVA(同 Kruskal-Wallis)へ進むのが定石です。 箱ひげ図は「まず眺めて仮説を立てる」EDA、 検定は「その仮説を確かめる」確認統計、 という役割分担で使い分けます。