K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

モザイクプロットでカテゴリの交差割合を面積で表す

中級

06.7.32

モザイクプロットでカテゴリの交差割合を面積で表す

最終更新 2026-03-06 読了時間 2 分
まとめ
モザイクプロットは、2 つのカテゴリ変数のクロス集計表(分割表)を「横幅 = 行カテゴリの割合、高さ = その行の中での列カテゴリの割合」の矩形に変えて、セルの面積が観測度数に比例するように描いた図です。もし 2 変数に関連がなければ、すべての列で高さの区切りが同じ位置に並び、関連があるほど区切りがずれて見えます。statsmodels の mosaic(df, ["年代", "利用サービス"]) が DataFrame の 2 列から直接この矩形を描き、properties 引数でセルごとの色を指定できます。読み終えると、20 代の列で SNS アプリの矩形が高く、50 代の列で EC が高いという「年代による嗜好の違い」を区切り位置のずれとして読み取り、カイ二乗検定の結果を図で補足できるようになります。

直感 #

アンケートで 500 人の年代と「最もよく使うサービス」を聞き、4 × 4 のクロス集計表を作ったとします。16 個の数字から「若い人ほど SNS、年上ほど EC」と読むには比較が要ります。モザイクプロットは、まず横方向を年代で切り、30 代が最も多ければ 30 代の列を最も広く取り、次に各列の中を、その年代の人がどのサービスを選んだかの割合で縦に切ります。関連がなければ区切りはそろいます。年代とサービスに関連があると区切りの高さが列ごとにずれ、そのずれの大きさがカイ二乗統計量の大きさに対応します。

Python で描く #

次のコードは、年代ごとに異なる確率でサービスを選ぶ 500 人分のデータを作り、mosaic でクロス集計を矩形に変えて描きます。年代が上がるほど同じサービスの色を濃くする color_map を properties で渡しています。

 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
42
43
44
45
46
47
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.graphics.mosaicplot import mosaic

rng = np.random.default_rng(42)

n = 500
age_group = rng.choice(["20代", "30代", "40代", "50代"], size=n, p=[0.25, 0.30, 0.25, 0.20])
# 年代によって好みが異なるように設定
prefs = {
    "20代": [0.45, 0.30, 0.15, 0.10],
    "30代": [0.25, 0.35, 0.25, 0.15],
    "40代": [0.15, 0.25, 0.35, 0.25],
    "50代": [0.10, 0.15, 0.30, 0.45],
}
product = np.array([
    rng.choice(["SNSアプリ", "動画配信", "ニュース", "EC"], p=prefs[ag])
    for ag in age_group
])

df = pd.DataFrame({"年代": age_group, "利用サービス": product})

# 色の設定
color_map = {
    ("20代", "SNSアプリ"): "#93c5fd", ("20代", "動画配信"): "#86efac",
    ("20代", "ニュース"): "#fde68a", ("20代", "EC"): "#fca5a5",
    ("30代", "SNSアプリ"): "#60a5fa", ("30代", "動画配信"): "#4ade80",
    ("30代", "ニュース"): "#fbbf24", ("30代", "EC"): "#f87171",
    ("40代", "SNSアプリ"): "#3b82f6", ("40代", "動画配信"): "#22c55e",
    ("40代", "ニュース"): "#f59e0b", ("40代", "EC"): "#ef4444",
    ("50代", "SNSアプリ"): "#2563eb", ("50代", "動画配信"): "#16a34a",
    ("50代", "ニュース"): "#d97706", ("50代", "EC"): "#dc2626",
}

fig, ax = plt.subplots(figsize=(10, 6))
mosaic(
    df, ["年代", "利用サービス"], ax=ax,
    properties=lambda key: {"color": color_map.get(key, "#cccccc")},
    gap=0.015,
    title="年代 × 利用サービスの構成(モザイクプロット)",
)
ax.set_xlabel("年代")
ax.set_ylabel("利用サービス")

fig.tight_layout()
plt.show()

年代と利用サービスの構成を示すモザイクプロット

実行結果の読み方 #

列の横幅が年代の人数の割合で、30 代の列が最も広いのは p=[0.25, 0.30, 0.25, 0.20] で 30 代を最多にしたためです。各列の中の矩形の高さが、その年代の中でのサービスの割合です。20 代の列では青(SNS アプリ)が最も高く、50 代の列では赤(EC)が最も高いので、列をまたいで区切りの位置が大きくずれています。このずれが「年代とサービスの選択に関連がある」ことの視覚的な証拠で、scipy.stats.chi2_contingency のカイ二乗検定で p 値を添えれば定量的な裏付けになります。

使いどころと落とし穴 #

モザイクプロットは、アンケートの年代別嗜好や AB テストのセグメント別結果のように、2 つのカテゴリ変数の関連をカイ二乗検定とセットで見せたいときに向いています。カテゴリ数が片方でも 6 を超えるとセルが細くなってラベルが読めません。その場合は少数カテゴリを「その他」にまとめるか、グループ化棒グラフに切り替えます。3 変数以上は mosaic に 3 列渡せば描けますが、入れ子が深くなるので 2 変数ずつ複数枚に分けるのが実用的です。色は 1 つの変数(ここではサービス)に割り当て、もう 1 つの変数は軸の位置で区別するのが基本で、両方に色を使うと 16 色の区別を読者に強いることになります。独立性からの乖離をセルごとに見せたいなら、期待度数との差(残差)を色にした残差ヒートマップが代替です。