論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
クラスタリング
Clustering
教師ラベルなしで、似たサンプル同士をグループ(クラスタ)に分ける手法の総称。
教師なし学習clusteringクラスタリングクラスタ分析

🔖 キーワード索引(補強・追加分)

クラスタリング 関連の補強キーワード。 クリックで該当箇所へ:

k-means 階層クラスタリング DBSCAN シルエット係数 エルボー法 ギャップ統計量 UMAP HDBSCAN GMM スペクトラル

🔖 キーワード索引 — 完全強化版

「クラスタリング」を理解するうえで必要なキーワードを 10 件以上提示します。 各チップから対応セクションへ移動できます。

30 秒結論 文脈 直感 数式 記号読み解き 実値計算 Python 実装 落とし穴 関連手法 関連用語 グループ教材 概念マップ

💡 30秒で分かる結論

🍰 まずはやさしく

似たもの同士を分けるグループ分けです。

データの自然なまとまりを見つけるために使います。

スマホのアプリで好みの曲を分けるようなものです。

ここでは代表的なやり方や注意点を読みます。

📖 もっと詳しく

クラスタリング(clustering)は、 「似たサンプル同士を自動でグループ化する」教師なし学習の代表的タスク。 「正解ラベル」を一切与えず、 データの内部構造をアルゴリズムが自力で発見します。

教師あり学習との違い

クラスタリングは「データの中に自然なグループはあるか?あるならどんな?」という探索的な問いに使います。 正解がないので、 結果の「正しさ」を評価するのが難しい一方、 仮説生成・データの初期理解で強力な道具になります。

🎯 クラスタリングが活躍する場面

📋 クラスタリング手法の分類

クラスタリング手法は次の4大カテゴリに分かれます:

分類仕組み代表例向くデータ
分割型(partitional)k個の中心への距離で分割k-means, k-medoids球形クラスタ、 大規模
階層型(hierarchical)点を段階的に結合 or 分割Ward法, 単連結, 完全連結中規模、 階層構造あり
密度ベース密な領域をクラスタとみなすDBSCAN, OPTICS, Mean Shift非球形、 ノイズあり
モデルベース確率分布の混合と仮定GMM, EM, ベイズクラスタ確率的所属、 重なり許容

🔬 距離の選択が決定的に重要

クラスタリングの中核は「2つのサンプルがどれだけ似ているか」の数値化。 これを「距離」(または類似度)と呼びます:

選択の指針

⚠️ 標準化が必須の理由

単位の違う変数を混ぜると、 桁の大きい変数だけで距離が決まり、 他の変数の情報が消えます。 例えば「人口(万人)」と「失業率(%)」をユークリッド距離で測れば、 人口の差が圧倒的に大きいため、 結果は実質「人口」だけでクラスタリングしたものに。

対策:必ず標準化(z-score化)してから距離計算。 sklearn.preprocessing.StandardScaler で平均0・分散1に揃える。 これで全変数が「平等」に効く。

🎯 クラスタ数 k の決定

k-means のような分割型では「何個のクラスタに分けるか」を事前に決める必要があります。 客観的指標:

📊 47都道府県でのクラスタリング例

「死亡率」「高齢化率」「保健医療費」「転入率」の4変数で47都道府県を k-means(k=4)でクラスタリングすると、 典型的に次のような結果になります:

これがクラスタリングの典型的な使い方。 「データ駆動で47都道府県を4類型に分類した」という客観的根拠として、 政策資料に使えます。

🔍 クラスタリング結果の評価

「正解」がないので、 結果の良さを評価するのが難しい。 主な評価手法:

内部評価(ラベルなしで評価):

外部評価(真のラベルがある場合):

解釈の難しさ:クラスタが見つかっても、 「なぜそうグルーピングされたか」「各クラスタの特徴は何か」は別途分析が必要。 クラスタごとの変数の平均値・標準偏差・代表サンプルを確認するのが定石。

💡 30 秒で分かる結論 — 完全強化版

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

🍰 まずはやさしく

データの共通点を探す分析手法のことです。

正解がないデータからグループを作りたいときに使います。

都道府県を特徴で分けるときなどに役立ちます。

この手法が分析の中でどう使われるかを読みます。

論文で「クラスタリング」「クラスタ分析」「教師なし学習で分類」と書かれているとき。 「47都道府県を似ているグループに自動分類」「顧客を購買行動で分類」など、 ラベルなしデータから自然なグループ構造を発見する手法の総称。

クラスタリング とは:教師ラベルなしで、似たサンプル同士をグループ(クラスタ)に分ける手法の総称。

📍 文脈ボックス — あなたが今見ているもの(完全強化版)

本ページは クラスタリング を、 統計データ分析コンペティション 2026 の再現教材として整理した用語ページです。 中核用語のひとつで、 47都道府県の人口・高齢化率・消費支出を用いたクラスタ分析 という具体的観点で SSDSE-B-2026(47 都道府県 × 複数年 × 100 超列)に紐づき、 実データを使って手を動かしながら理解できる構成にしています。

位置づけ:相関線形回帰仮説検定 といった基礎用語群と並列であり、応用としては 内生性IVDIDクラスタリング 等へ繋がります。

🎨 直感で掴む

🍰 まずはやさしく

データから自然なグループを作るイメージです。

データの傾向を分かりやすく整理するために使います。

部活のメンバーを能力や性格で分けるようなものです。

状況に合わせて手法を選ぶ流れについて読みます。

クラスタリング
k-means による47都道府県のクラスタリング(k=4)の例。 × が各クラスタの中心。 データ駆動の地域類型化。

図は典型的なクラスタリング結果。 4つの「自然なグループ」がデータから自動的に発見されています。 各クラスタには地理的・経済的に類似した県が集まり、 「データ駆動の分類」として解釈可能。

大事なポイント:このクラスタは「用意した変数(死亡率、 高齢化率)に基づく類似度」での分類。 違う変数(例えば気温や産業構造)で実行すれば、 違う分類が出ます。 「絶対的な真の分類」ではなく、 「分析者の選択を反映した類型化」なのが本質。

🎓 手法選択の判断フロー

「どの手法を使うべきか」迷ったときの判断フロー:

① クラスタ数が事前に決まっているか?

② データの形状は球形に近いか?

③ ノイズや外れ値が多いか?

④ サンプル数は?

⑤ 結果の階層構造を見たいか?

⑥ 確率的な所属(「東京は60%大都市、 40%中核」)が欲しいか?

複数手法の比較が定石

1つの手法だけで結論を出さず、 複数手法で結果を比較し、 安定して同じグループが出るなら信頼性が高い。 「k-means と Ward法 で同じクラスタ構造が見えた」のような頑健性チェックが論文の品質を上げます。

クラスタリングの限界と批判

クラスタリングには本質的な限界もあります:

これらの限界を意識した上で、 「データを整理する道具」として使うのが正しい使い方。 「客観的真実を発見する魔法」ではない。

🎨 直感で掴む — 完全強化版

クラスタリング を一言でいえば「47都道府県の人口・高齢化率・消費支出を用いたクラスタ分析」。 47 都道府県という小さな母集団でも、 SSDSE-B-2026 の A1101 列に注目すると、 大都市圏と地方の差・人口規模に伴う相対比較など、 様々なパターンが見えてきます。

比喩でいうと、 クラスタリング はデータ分析の「眼鏡」のようなもの。 同じデータでも眼鏡を変えれば、 平均(中心)・分散(ばらつき)・相関(連動)・因果(影響)と、 異なる情報が浮かび上がります。 SSDSE-B-2026 を題材に、 この眼鏡をかけてみるのが本ページの狙いです。

📐 数式または定義 — 完全強化版

🍰 まずはやさしく

似ている点同士を同じ群に分ける計算です。

「似ている」ことを数値で表すために使います。

買い物での支出額が近い人をまとめるようなものです。

計算の仕組みや距離の測り方について読みます。

クラスタリングは「データ点の集合 $\{x_1, ..., x_n\}$ を、 互いに似た点が同じ群に入るように $K$ 個のクラスタ $C_1, ..., C_K$ に分割する」問題。 「似ている」の数値化 (距離) と「最適化目的」(評価指標) の組合せが手法を決める。

1. k-means の目的関数

最も基本的な分割型クラスタリング(詳細は k-means法 のページ参照)。 各クラスタ $C_k$ の中心 $\mu_k$ と所属点の距離二乗和 (SSE; Sum of Squared Errors) を最小化:

$$ \arg\min_{\{C_k\},\{\mu_k\}} \sum_{k=1}^K \sum_{x \in C_k} \lVert x - \mu_k \rVert^2 \quad \text{where} \quad \mu_k = \frac{1}{|C_k|} \sum_{x \in C_k} x $$

$\mu_k$ はクラスタ $k$ の重心 (centroid)。 反復で「最近傍割当て → 重心更新」を繰り返す (Lloyd アルゴリズム)。

2. 距離 (類似度) の定義

「2 点間がどれだけ似ているか」を測る距離 $d(x, y)$ の代表例:

3. 階層クラスタリングの距離 (リンケージ)

2 つのクラスタ $A, B$ 間の距離 $D(A, B)$ をどう定義するかでアルゴリズムが分かれる(詳細は 階層クラスタリングWard法デンドログラム の各ページ参照):

4. DBSCAN の密度ベース定義

点 $p$ を中心とする半径 $\varepsilon$ の球内に $\text{MinPts}$ 個以上の点が含まれるとき、 $p$ を コア点 (core point) と呼ぶ:

$$ N_\varepsilon(p) = \{q \mid d(p, q) \leq \varepsilon\}, \quad |N_\varepsilon(p)| \geq \text{MinPts} \implies p \in \text{Core} $$

コア点同士が密度到達可能 (density-reachable) なら同じクラスタ。 いずれのコア点からも到達できない点はノイズ (外れ値) 扱い。

5. クラスタ評価指標

結果クラスタリングの良さを定量化する代表的指標:

6. Gaussian Mixture Model (GMM) の確率モデル

$K$ 個の多変量正規分布の混合として、 各点の所属を確率で表す:

$$ p(x) = \sum_{k=1}^K \pi_k\, \mathcal{N}(x \mid \mu_k, \Sigma_k), \quad \sum_{k=1}^K \pi_k = 1 $$

$\pi_k$ は事前確率、 $\Sigma_k$ は共分散行列。 EM アルゴリズムで最尤推定。 ベイズの定理で各点 $x$ の所属確率 (responsibility) を計算: $$ \gamma_k(x) = \frac{\pi_k\, \mathcal{N}(x \mid \mu_k, \Sigma_k)}{\sum_{j=1}^K \pi_j\, \mathcal{N}(x \mid \mu_j, \Sigma_j)} $$

k-means は GMM で $\Sigma_k = \sigma^2 I$ (等方分散) かつ硬い割当 ($\gamma_k \in \{0,1\}$) とした特殊ケース。

これら 6 種の数式群を理解すれば、 「何を最小化しているか」「どの仮定が崩れたら別手法に切り替えるか」という選択判断が可能になる。 詳細な記号解説は次節で行う。

🔬 数式を言葉で読み解く — 完全強化版

前節の数式に登場した各記号を、日本語の意味に変換します。

🧮 SSDSE-B 実値計算例(47都道府県データ)

47 都道府県を経済指標 5 次元でクラスタリングし、 地域類型を抽出する完全例。 標準化+ k-means/階層/DBSCAN を比較。

① 計算コード

🎯 このコードでやること:クラスタリング — 教師なし学習による群構造発見に関連するステップ #4。主要な指標(係数・統計量・スコア)を算出します。

📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023年度の47都道府県
# 総人口・高齢化率・消費支出・診療所数(人口10万対)・標準地価 の5指標でクラスタリング
# 標準化後の X (shape=(47, 5)):
#   pref      A1101  高齢化率  消費支出  診療所数  C5401
# 0 北海道     0.88   0.43    0.04   -1.50   -0.49
# 1 青森県    -0.53   1.10   -1.36   -1.08   -0.61
# 2 岩手県    -0.54   1.03    0.11   -0.76   -0.45
 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
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
from sklearn.decomposition import PCA

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)  # 2023年度の47都道府県
df['高齢化率'] = df['A1303'] / df['A1101'] * 100      # 高齢人口 / 総人口 (%)
df['消費支出'] = df['L3221'] / 1000                   # 二人以上の世帯、 千円/月
df['診療所数'] = df['I5102'] / df['A1101'] * 100000   # 人口10万人あたり
features = ['A1101', '高齢化率', '消費支出', '診療所数', 'C5401']  # A1101=総人口、 C5401=標準地価
X = StandardScaler().fit_transform(df[features])

# エルボー法
inertias, silhouettes = [], []
ks = range(2, 11)
for k in ks:
    km = KMeans(n_clusters=k, random_state=42, n_init=10).fit(X)
    inertias.append(km.inertia_)
    silhouettes.append(silhouette_score(X, km.labels_))

fig, axes = plt.subplots(1, 2, figsize=(11, 4))
axes[0].plot(ks, inertias, 'o-'); axes[0].set_xlabel('k'); axes[0].set_ylabel('Inertia (SSE)')
axes[1].plot(ks, silhouettes, 'o-'); axes[1].set_xlabel('k'); axes[1].set_ylabel('Silhouette')
plt.tight_layout(); plt.savefig('cluster_elbow.png', dpi=110)

# シルエット最大は k=2 だが、 解釈しやすさから k=4 で確定
km = KMeans(n_clusters=4, random_state=42, n_init=10).fit(X)
df['cluster'] = km.labels_
print('クラスタごとの代表値:')
print(df.groupby('cluster')[features].mean().round(1))
📤 実行例(実行時の標準出力)
クラスタごとの代表値:
              A1101  高齢化率   消費支出   診療所数     C5401
cluster                                          
0         1003076.9  34.4  271.9   92.6   25023.1
1        14086000.0  22.8  341.3  105.7  404400.0
2         6374750.0  27.1  296.1   79.2  112800.0
3         1849160.0  31.9  306.4   81.1   37436.0
💬 読み方:算出された統計量を判定基準と比較し、有意性/効果量を評価する。

② 期待出力

項目 参考 解釈
kInertiaSilhouette解釈
2153.30.488都市圏/地方 二分(シルエット最大)
3119.50.248都市/中間/地方
489.10.268採用(解釈しやすさ重視)
574.30.272細分化(過剰)
クラスタ0青森・秋田・島根 など計13県高高齢化・低地価高齢化地方群
クラスタ1東京都(単独)超高地価・低高齢化大都市
クラスタ2神奈川・大阪・福岡 など計8府県高人口・高地価大都市圏
クラスタ3北海道・宮城・広島 など計25道府県各指標が中位中間群

👉 値は SSDSE-B-2026(2023年度)に上記コードを実行した実測値。 同じ手順で他年度・他変数にも適用可能。

🧮 実値で計算してみる — SSDSE-B-2026 で クラスタリング(完全強化版)

SSDSE-B-2026(公的統計の社会・教育系データセット、 47 都道府県 × 10 年分超 × 100 以上の列)を用いて、 「クラスタリング」を体感します。 ファイル名は SSDSE-B-2026.csv、 読み込みは下記の Python コードで行います。

1
2
3
4
5
6
7
8
import pandas as pd

# SSDSE-B-2026 を読み込む(cp932 / Shift_JIS)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
print(df.shape)          # (564, 112)
print(df['SSDSE-B-2026'].unique())  # 含まれる年度
latest = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()].copy()
print(latest[['Prefecture', 'A1101', 'A4101']].head())

ここで使った中心列 A1101 は SSDSE-B-2026 における 47都道府県の人口・高齢化率・消費支出を用いたクラスタ分析 に関連する指標です。 算出例:

🧮 数式に値を入れて手で計算する — k-means の重心更新

合成データ 3 点と初期重心 (3,3) を代入し、 k-means の重心更新式 c_k = (1/|C_k|) Σ x_i を Step 1〜4 で 1 step ぶん手計算する。 同じ計算を Python(scikit-learn)で再現し、 結果が完全一致することを確認する。

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

$$ c_k \;=\; \frac{1}{|C_k|}\sum_{x_i \in C_k} x_i, \qquad d(x_i, c_k) = \sqrt{\sum_j (x_{ij} - c_{kj})^2} $$

c_k はクラスタ k の重心、 C_k はクラスタ k に割り当てられた点集合。 各点を最近傍重心に割り当て、 重心を再計算するのが 1 step。

Step 1: データ準備(点 3 個 + 初期重心 1 個)

xy
P123
P245
P378
初期重心 c33

k=1(重心 1 個)の最小例。 すべての点が唯一のクラスタに属する。

Step 2: 各点から初期重心までの距離(参考)

計算(ユークリッド距離)結果
P1→c√((2-3)² + (3-3)²) = √11.0000
P2→c√((4-3)² + (5-3)²) = √52.2361
P3→c√((7-3)² + (8-3)²) = √416.4031

距離は割当てには使わない(k=1 なので全点同一クラスタ)が、 重心が初期位置からどれだけ動くかの直感を与える。

Step 3: 重心更新(重心の式に代入)

成分計算結果
新重心 c_x(2 + 4 + 7) / 34.3333
新重心 c_y(3 + 5 + 8) / 35.3333
新重心 c'(4.3333, 5.3333)

Step 4: 移動量と SSE(クラスタ内平方和)

項目計算結果
重心移動量√((4.3333-3)² + (5.3333-3)²) = √(1.7778+5.4445)2.6874
SSE = Σ d(x_i, c')²(2-4.3333)²+(3-5.3333)² + (4-4.3333)²+(5-5.3333)² + (7-4.3333)²+(8-5.3333)²25.3333

重心が (3,3) から (4.33, 5.33) へ約 2.69 移動。 SSE は 25.33(= 5.4444+5.4444+0.1111+0.1111+7.1111+7.1111)で、 これが次の step(k=1 だと最終)の目的関数値。

🐍 同じ計算を Python で再現

 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 numpy as np
from sklearn.cluster import KMeans

# Step 1: データ
X = np.array([[2,3], [4,5], [7,8]])
c_init = np.array([[3,3]])

# Step 2: 初期重心までの距離(手計算照合)
d = np.linalg.norm(X - c_init, axis=1)
print(f"初期重心までの距離: {d.round(4).tolist()}")

# Step 3: 新重心(k=1 なので単純平均)
c_new = X.mean(axis=0)
print(f"新重心 c' = {c_new}")

# Step 4: 移動量と SSE
shift = np.linalg.norm(c_new - c_init[0])
sse = ((X - c_new)**2).sum()
print(f"重心移動量 = {shift:.4f}")
print(f"SSE        = {sse:.4f}")

# sklearn KMeans で同等確認(k=1、 初期重心 (3,3))
km = KMeans(n_clusters=1, init=c_init, n_init=1, max_iter=1, random_state=0)
km.fit(X)
print(f"KMeans 収束後重心: {km.cluster_centers_}")
print(f"KMeans inertia (SSE): {km.inertia_:.4f}")

📤 実行結果

初期重心までの距離: [1.0, 2.2361, 6.4031] 新重心 c' = [4.33333333 5.33333333] 重心移動量 = 2.6874 SSE = 25.3333 KMeans 収束後重心: [[4.33333333 5.33333333]] KMeans inertia (SSE): 25.3333

💬 手計算(Step 2: 距離 1.0/2.236/6.403、 Step 3: 重心 (4.333, 5.333)、 Step 4: 移動量 2.687, SSE 25.333)と numpy 出力・sklearn KMeans の収束結果がすべて完全一致。 k-means の 1 step は「最近傍割当て → 重心平均」の単純反復であることが体感できる。

🐍 Python での実装

① scikit-learn での基本

🎯 このコードでやること:クラスタリング — 教師なし学習による群構造発見に関連するステップ #1。最初のスニペットです。SSDSE-B-2026 を読み込みます。

📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023年度の47都道府県
# 総人口・高齢化率・消費支出・診療所数(人口10万対)・標準地価 の5指標でクラスタリング
# 標準化後の X (shape=(47, 5)):
#   pref      A1101  高齢化率  消費支出  診療所数  C5401
# 0 北海道     0.88   0.43    0.04   -1.50   -0.49
# 1 青森県    -0.53   1.10   -1.36   -1.08   -0.61
# 2 岩手県    -0.54   1.03    0.11   -0.76   -0.45
 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
# ── クラスタリングの例で使うデータを用意します(47 都道府県)──
import pandas as pd
from sklearn.preprocessing import StandardScaler

_df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
_df = _df[_df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
_df['年度'] = pd.to_numeric(_df['年度'], errors='coerce')
_df = _df[_df['年度'] == _df['年度'].max()]
_feats = ['総人口', '出生数', '死亡数', '消費支出(二人以上の世帯)']
for _c in _feats:
    _df[_c] = pd.to_numeric(_df[_c], errors='coerce')
_df = _df.dropna(subset=_feats)
X = _df[_feats].values
X_std = StandardScaler().fit_transform(X)
labels_index = _df['都道府県'].values

from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
import pandas as pd
import numpy as np

# データの標準化(重要!)
scaler = StandardScaler()
X_std = scaler.fit_transform(X)

# k-means
km = KMeans(n_clusters=3, random_state=0, n_init=10)
labels_km = km.fit_predict(X_std)
print(f'クラスタ中心: {km.cluster_centers_}')
print(f'inertia: {km.inertia_}')

# 階層クラスタリング(Ward法)
agg = AgglomerativeClustering(n_clusters=3, linkage='ward')
labels_agg = agg.fit_predict(X_std)

# シルエットスコアで評価
score = silhouette_score(X_std, labels_km)
print(f'シルエットスコア: {score:.3f}')
📤 実行例(実行時の標準出力)
クラスタ中心: [[-0.55266695 -0.52186218 -0.57999927 -1.23236363]
 [ 1.80241337  1.79067417  1.85344621  0.43471069]
 [-0.361482   -0.37327437 -0.36564101  0.48433324]]
inertia: 49.7004245393956
シルエットスコア: 0.463
💬 読み方:このステップは前処理/補助関数。本処理は次のスニペットに続く。

② 最適クラスタ数の探索

🎯 このコードでやること:クラスタリング — 教師なし学習による群構造発見に関連するステップ #2。基本統計量を計算します。

📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023年度の47都道府県
# 総人口・高齢化率・消費支出・診療所数(人口10万対)・標準地価 の5指標でクラスタリング
# 標準化後の X (shape=(47, 5)):
#   pref      A1101  高齢化率  消費支出  診療所数  C5401
# 0 北海道     0.88   0.43    0.04   -1.50   -0.49
# 1 青森県    -0.53   1.10   -1.36   -1.08   -0.61
# 2 岩手県    -0.54   1.03    0.11   -0.76   -0.45
 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
# ── クラスタリングの例で使うデータを用意します(47 都道府県)──
import pandas as pd
from sklearn.preprocessing import StandardScaler

_df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
_df = _df[_df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
_df['年度'] = pd.to_numeric(_df['年度'], errors='coerce')
_df = _df[_df['年度'] == _df['年度'].max()]
_feats = ['総人口', '出生数', '死亡数', '消費支出(二人以上の世帯)']
for _c in _feats:
    _df[_c] = pd.to_numeric(_df[_c], errors='coerce')
_df = _df.dropna(subset=_feats)
X = _df[_feats].values
X_std = StandardScaler().fit_transform(X)
labels_index = _df['都道府県'].values

import matplotlib.pyplot as plt

inertias = []
silhouettes = []
for k in range(2, 11):
    km = KMeans(n_clusters=k, random_state=0, n_init=10).fit(X_std)
    inertias.append(km.inertia_)
    silhouettes.append(silhouette_score(X_std, km.labels_))

# エルボー法
plt.subplot(1, 2, 1)
plt.plot(range(2, 11), inertias, 'o-')
plt.xlabel('k'); plt.ylabel('inertia')

# シルエット法
plt.subplot(1, 2, 2)
plt.plot(range(2, 11), silhouettes, 'o-')
plt.xlabel('k'); plt.ylabel('Silhouette')
📤 このブロックは標準出力には何も出さない
💬 読み方:数値が出力されたら、まず大きさ(オーダー)と符号を確認しよう。

③ デンドログラムの描画

🎯 このコードでやること:クラスタリング — 教師なし学習による群構造発見に関連するステップ #3。可視化(散布図/樹形図/時系列プロット)を描きます。

📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023年度の47都道府県
# 総人口・高齢化率・消費支出・診療所数(人口10万対)・標準地価 の5指標でクラスタリング
# 標準化後の X (shape=(47, 5)):
#   pref      A1101  高齢化率  消費支出  診療所数  C5401
# 0 北海道     0.88   0.43    0.04   -1.50   -0.49
# 1 青森県    -0.53   1.10   -1.36   -1.08   -0.61
# 2 岩手県    -0.54   1.03    0.11   -0.76   -0.45
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ── クラスタリングの例で使うデータを用意します(47 都道府県)──
import pandas as pd
from sklearn.preprocessing import StandardScaler

_df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
_df = _df[_df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
_df['年度'] = pd.to_numeric(_df['年度'], errors='coerce')
_df = _df[_df['年度'] == _df['年度'].max()]
_feats = ['総人口', '出生数', '死亡数', '消費支出(二人以上の世帯)']
for _c in _feats:
    _df[_c] = pd.to_numeric(_df[_c], errors='coerce')
_df = _df.dropna(subset=_feats)
X = _df[_feats].values
X_std = StandardScaler().fit_transform(X)
labels_index = _df['都道府県'].values

from scipy.cluster.hierarchy import linkage, dendrogram

Z = linkage(X_std, method='ward')
plt.figure(figsize=(14, 6))
dendrogram(Z, labels=df['Prefecture'].values, leaf_rotation=90)
plt.show()
📤 このブロックは標準出力には何も出さない
💬 読み方:プロットの形状から定性的な傾向(単調性・周期性)を読み取る。

🚧 クラスタリングの落とし穴

  1. 標準化を忘れる:単位の大きい変数が支配的に
  2. 「クラスタが見えるはず」と思い込む:データに構造がないこともある
  3. 距離指標が不適切:ドメインに合わせて選ぶ
  4. k-means は局所最適:n_init を複数回試す
  5. 解釈なきクラスタ:結果に意味付けして初めて使える
  6. 高次元の呪い:高次元では距離が無意味化、 PCA等で削減

💼 実務での応用例

📜 クラスタリングの歴史

🐍 Python 実装バリエーション(scikit-learn / scipy / Optuna)

A. scikit-learn による実装

🎯 このコードでやること:クラスタリング — 教師なし学習による群構造発見に関連するステップ #5。仮説検定・モデル評価を行います。

📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023年度の47都道府県
# 総人口・高齢化率・消費支出・診療所数(人口10万対)・標準地価 の5指標でクラスタリング
# 標準化後の X (shape=(47, 5)):
#   pref      A1101  高齢化率  消費支出  診療所数  C5401
# 0 北海道     0.88   0.43    0.04   -1.50   -0.49
# 1 青森県    -0.53   1.10   -1.36   -1.08   -0.61
# 2 岩手県    -0.54   1.03    0.11   -0.76   -0.45
 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
# ── クラスタリングの例で使うデータを用意します(47 都道府県)──
import pandas as pd
from sklearn.preprocessing import StandardScaler

_df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
_df = _df[_df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
_df['年度'] = pd.to_numeric(_df['年度'], errors='coerce')
_df = _df[_df['年度'] == _df['年度'].max()]
_feats = ['総人口', '出生数', '死亡数', '消費支出(二人以上の世帯)']
for _c in _feats:
    _df[_c] = pd.to_numeric(_df[_c], errors='coerce')
_df = _df.dropna(subset=_feats)
X = _df[_feats].values
X_std = StandardScaler().fit_transform(X)
labels_index = _df['都道府県'].values

from sklearn.cluster import KMeans, DBSCAN
from sklearn.mixture import GaussianMixture
from sklearn.metrics import silhouette_score, adjusted_rand_score

# 複数手法の比較
results = {}
for name, model in [
    ('KMeans-k4', KMeans(n_clusters=4, random_state=42, n_init=10)),
    ('GMM-k4',    GaussianMixture(n_components=4, random_state=42)),
    ('DBSCAN',    DBSCAN(eps=1.2, min_samples=3)),
    ('Hierarchical', AgglomerativeClustering(n_clusters=4, linkage='ward')),
]:
    labels = model.fit_predict(X_std)   # 標準化後のデータで比較する
    if len(set(labels)) > 1 and -1 not in labels:
        sil = silhouette_score(X_std, labels)
        results[name] = (labels, sil)
        print(f'{name:18} Silhouette = {sil:.3f}')

# 手法間の一致度
km_lab = results['KMeans-k4'][0]
for name in ['GMM-k4', 'Hierarchical']:
    if name in results:
        ari = adjusted_rand_score(km_lab, results[name][0])
        print(f'KMeans vs {name}: ARI = {ari:.3f}')
📤 実行例(実行時の標準出力)
KMeans-k4          Silhouette = 0.469
GMM-k4             Silhouette = 0.378
Hierarchical       Silhouette = 0.469
KMeans vs GMM-k4: ARI = 0.637
KMeans vs Hierarchical: ARI = 1.000
💬 読み方:p 値や信頼区間と合わせて読み、効果の有無+大きさを両輪で判断する。

B. scipy / statsmodels による実装

🎯 このコードでやること:クラスタリング — 教師なし学習による群構造発見に関連するステップ #6。結果を整形して表示します。

📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023年度の47都道府県
# 総人口・高齢化率・消費支出・診療所数(人口10万対)・標準地価 の5指標でクラスタリング
# 標準化後の X (shape=(47, 5)):
#   pref      A1101  高齢化率  消費支出  診療所数  C5401
# 0 北海道     0.88   0.43    0.04   -1.50   -0.49
# 1 青森県    -0.53   1.10   -1.36   -1.08   -0.61
# 2 岩手県    -0.54   1.03    0.11   -0.76   -0.45
 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
# ── クラスタリングの例で使うデータを用意します(47 都道府県)──
import pandas as pd
from sklearn.preprocessing import StandardScaler

_df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
_df = _df[_df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
_df['年度'] = pd.to_numeric(_df['年度'], errors='coerce')
_df = _df[_df['年度'] == _df['年度'].max()]
_feats = ['総人口', '出生数', '死亡数', '消費支出(二人以上の世帯)']
for _c in _feats:
    _df[_c] = pd.to_numeric(_df[_c], errors='coerce')
_df = _df.dropna(subset=_feats)
X = _df[_feats].values
X_std = StandardScaler().fit_transform(X)
labels_index = _df['都道府県'].values

from scipy.cluster.hierarchy import linkage, dendrogram, fcluster
from scipy.spatial.distance import pdist
import matplotlib.pyplot as plt

# 階層クラスタリング + デンドログラム
Z = linkage(X, method='ward')
fig, ax = plt.subplots(figsize=(14, 5))
dendrogram(Z, labels=df['Prefecture'].values, leaf_rotation=90, ax=ax)
ax.axhline(y=6.5, color='r', linestyle='--', label='切り捨て位置 (k=4)')
ax.legend(); plt.tight_layout()
plt.savefig('dendrogram.png', dpi=110)

# 階層から flat なクラスタを取り出す
labels_hier = fcluster(Z, t=4, criterion='maxclust')
print('階層クラスタリング結果:', np.bincount(labels_hier))
📤 実行例(実行時の標準出力)
階層クラスタリング結果: [ 0 21 16  9  1]
💬 読み方:表示された数値テーブルから個別の都道府県の位置づけを読み取る。

C. Optuna でハイパラ・選択最適化

🎯 このコードでやること:クラスタリング — 教師なし学習による群構造発見に関連するステップ #7。47都道府県データに当てはめて確認します。

📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]  # 2023年度の47都道府県
# 総人口・高齢化率・消費支出・診療所数(人口10万対)・標準地価 の5指標でクラスタリング
# 標準化後の X (shape=(47, 5)):
#   pref      A1101  高齢化率  消費支出  診療所数  C5401
# 0 北海道     0.88   0.43    0.04   -1.50   -0.49
# 1 青森県    -0.53   1.10   -1.36   -1.08   -0.61
# 2 岩手県    -0.54   1.03    0.11   -0.76   -0.45
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import optuna
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

# クラスタ数 + アルゴリズムを最適化
def objective(trial):
    algo = trial.suggest_categorical('algo', ['kmeans', 'ward', 'gmm'])
    k = trial.suggest_int('k', 2, 8)
    if algo == 'kmeans':
        m = KMeans(n_clusters=k, random_state=42, n_init=10)
    elif algo == 'ward':
        m = AgglomerativeClustering(n_clusters=k, linkage='ward')
    else:
        m = GaussianMixture(n_components=k, random_state=42)
    labels = m.fit_predict(X)
    if len(set(labels)) > 1:
        return -silhouette_score(X, labels)
    return 0

study = optuna.create_study(direction='minimize')
study.optimize(objective, n_trials=30)
print('Best:', study.best_params, ' Silhouette:', -study.best_value)
📤 実行例(実測)
Best: {'algo': 'gmm', 'k': 2}  Silhouette: 0.7914643636382689
💬 読み方:SSDSE-B-2026 の実値に当てはめると教科書例より分散が大きいことに注意。

D. ライブラリ早見表

ライブラリ / 関数 用途
sklearn.cluster.KMeans最も標準的
sklearn.cluster.AgglomerativeClustering階層クラスタリング
sklearn.cluster.DBSCAN密度ベース
scipy.cluster.hierarchyデンドログラム描画
hdbscan階層 DBSCAN
yellowbrick.clusterシルエット可視化

🐍 Python 実装 — 完全強化版

scipy / pandas / scikit-learn / statsmodels を中心とした標準的な実装例です。 まず CSV を読み込み、 次に クラスタリング の解析を行います。

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

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()].copy()

x = df['A1101'].astype(float).values
y = df['A4101'].astype(float).values

# 基本統計量
print('n            =', len(x))
print('mean(x)      =', np.mean(x))
print('std(x)       =', np.std(x, ddof=1))

# クラスタリング の代表的計算(用途に応じて scipy/statsmodels を切替える)
r, p = stats.pearsonr(x, y)
print(f'Pearson r = {r:.4f}, p = {p:.4g}')
rs, ps = stats.spearmanr(x, y)
print(f'Spearman rho = {rs:.4f}, p = {ps:.4g}')

用途別の追加実装:

1
2
3
4
5
6
7
8
9
# 標準化と簡易クラスタリングの例
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

X = df[['A1101', 'A4101']].astype(float).values
Xs = StandardScaler().fit_transform(X)
km = KMeans(n_clusters=4, n_init=10, random_state=0).fit(Xs)
df['cluster'] = km.labels_
print(df[['Prefecture', 'A1101', 'A4101', 'cluster']].head(10))
1
2
3
4
5
6
7
# 時系列(北海道の A1101)— 例として ARIMA 系の前処理
import statsmodels.api as sm

ts = df.sort_values('SSDSE-B-2026').groupby('SSDSE-B-2026')['A1101'].mean()
print(ts.tail())
res = sm.tsa.stattools.adfuller(ts)
print('ADF stat:', res[0], 'p:', res[1])

⚠️ よくある落とし穴

❌ クラスタ構造の有無を確認せずに分ける
ランダムなデータでも k-means を実行すれば k 個のクラスタが出ます。 「クラスタが見つかった = 構造がある」ではない。 シルエット係数が低い(0.3未満)、 ランダムデータと比較してギャップが小さい場合、 そもそも構造がない可能性。 結論の前にこの確認が必須。
❌ 標準化を忘れる
単位の違う変数(人口、 失業率)を標準化せずクラスタリングすると、 桁の大きい変数だけで距離が決まる。 必ず StandardScaler を適用。 PCA で次元削減してからクラスタリングする手もある。
❌ k を恣意的に決める
「とりあえず k=3 でやりました」は学術論文では通用しない。 エルボー法、 シルエット係数、 ギャップ統計量で客観的に決定。 さらに事前理論との整合性を確認。
❌ 距離尺度を意識しない
ユークリッド距離が最も一般的だが、 データの性質によっては不適切。 文書類似度ならコサイン、 順位データならスピアマン的な距離、 カテゴリ混在ならガワー距離など。 デフォルトのままが必ずしも最良ではない。
❌ クラスタの解釈なしで終わる
「k=4 でクラスタリングしました」だけでは研究にならない。 各クラスタの変数の平均値・代表サンプル・特徴を解釈し、 「クラスタAは大都市群」のような命名と説明が必須。 解釈なしのクラスタリングは無意味。
❌ 1つの手法だけで結論
k-means で出たクラスタが Ward法でも同じか、 距離尺度を変えても安定か、 を確認するのが頑健性。 1手法だけの結果は偶然の産物かもしれない。
❌ 高次元データに直接適用
50次元以上のデータでは「次元の呪い」が発生し、 全点が等距離になりクラスタリングが意味を失う。 PCA や UMAP で次元削減してから、 または高次元向け手法(HDBSCAN等)を使う。

👁️ 直感 — クラスタリングは「似たもの同士を集める」

クラスタリング(clustering)は、 ラベルなしのデータを類似度に基づいて自動的にグループ分けする教師なし学習の一種。 「47都道府県を、 家計の似た傾向でグループ化」のような問題。

クラスタリング手法比較

🎯 主要な3つのアプローチ

① 分割クラスタリング

「k個に分ける」とあらかじめ決めて最適化。 代表:k-means、 k-medoids、 ファジー C-means。 計算は高速。

② 階層クラスタリング

近いペアから順にまとめていく(凝集型)、 または大きく分割(分割型)。 結果がデンドログラムに。 任意の数のクラスタが取れる。 代表:Ward法、 群平均法。

③ 密度ベース

「密度が高い領域がクラスタ」と考える。 代表:DBSCAN、 HDBSCAN、 OPTICS。 任意の形状を捉え、 ノイズ判別もできる。

その他

📏 距離指標 — 何を「似ている」とするか

距離 公式 使い時
ユークリッド距離√Σ(xᵢ-yᵢ)²標準。 連続値
マンハッタン距離Σ|xᵢ-yᵢ|高次元、 ロバスト
マハラノビス距離√((x-μ)ᵀΣ⁻¹(x-μ))変数の相関を考慮
コサイン距離1 - cos(x, y)テキスト、 ベクトル空間
Jaccard 距離1 - |X∩Y|/|X∪Y|集合データ

🎯 最適クラスタ数の決め方

クラスタ数判定

図の読み方:左(エルボー法)は k を増やすほど inertia が単調に減少するので、 「下がり方が急に緩くなる折れ目」(この図では k=3〜4 付近)を探す。 右(シルエット係数)は最大となる k を探すが、 この図のように最大が k=6 になるなど、 指標間で最適 k が割れることもある。 その場合は 1 つの指標を鵜呑みにせず、 解釈のしやすさ・事前知識も併せて総合的に決める。

① エルボー法

クラスタ内分散の合計(inertia)を k に対してプロット。 折れ目(elbow)が最適 k。

② シルエット法

各点が自クラスタ内でどれだけ凝集し、 他クラスタからどれだけ離れているかを測る。 1に近いほど良い。

③ Gap 統計量

ランダムデータと比較して「実データの方がクラスタ構造が強い」かを定量化。

④ ドメイン知識

業務的に「3地区に分けたい」「5セグメントが必要」と決まっている場合は、 それを優先する。

📊 クラスタリングの評価指標

内部評価(ラベルなし)

外部評価(正解ラベルあり)

⚠️ 落とし穴(拡張版・各 100 文字以上)

① 標準化を忘れる
単位が異なる変数(所得:千円、 高齢化率:%)をそのままクラスタリングすると、 値の桁が大きい変数だけで距離が決まる。 StandardScaler や RobustScaler で必ず前処理。 PCA で次元削減してからクラスタリングする手もある。 SSDSE のような混在単位データでは必須。
② k を恣意的に決める
「とりあえず k=3」は学術的に通用しない。 エルボー法・シルエット係数・ギャップ統計量・BIC(GMM の場合)など複数指標で評価。 さらに事前理論(「Big Five なら 5 因子」)と整合性を確認。 k は1つに決められない場合も多く、 k=3, 4, 5 を併示するのも実務的。
③ クラスタ構造の有無を確認しない
ランダムなデータでも k-means を実行すれば k 個のクラスタが出る。 「クラスタが見つかった = 構造がある」ではない。 シルエット係数が低い(0.3 未満)、 ランダムデータと比較してギャップが小さい場合、 そもそも構造がない可能性。 Hopkins 統計量・gap statistic で検定。
④ 距離尺度のデフォルトを盲信
ユークリッド距離が一般的だが、 文書類似度ならコサイン、 順位ならスピアマン、 カテゴリ混在ならガワー距離が適切。 デフォルトのままで結論を出さず、 距離尺度の選択を明示する。 マハラノビス距離は相関を考慮するため、 相関の強い変数群で有効。
⑤ クラスタの解釈なしで終わる
「k=4 でクラスタリングした」だけでは研究にならない。 各クラスタの変数の平均値・代表サンプル・特徴を解釈し、 「クラスタAは大都市群」のような命名と説明が必須。 解釈なしのクラスタリングは無意味で、 数値結果より定性的記述が重要。
⑥ 1 手法だけで結論を出す
k-means の結果が Ward 法でも同じか、 距離尺度を変えても安定か、 を確認するのが頑健性。 1 手法だけの結果は偶然の産物かもしれない。 ARI(Adjusted Rand Index)で複数手法の一致度を測る、 ブートストラップで安定性を評価。
⑦ 高次元データに直接適用
50 次元以上のデータでは「次元の呪い」で全点が等距離になりクラスタリングが意味を失う。 PCAUMAP次元削減してから適用、 もしくは高次元向け手法(HDBSCAN・スペクトラル)を使う。 SSDSE で全変数を投入する場合も次元削減を検討。
⑧ DBSCAN の eps を勘で決める
DBSCAN は eps(半径)と min_samples の 2 パラメータに敏感。 k 近傍距離プロット(k-distance plot)で eps を客観的に決める手法が一般的。 さらに HDBSCAN なら eps を不要にでき、 階層的なクラスタ階層が得られる。
⑨ クラスタ数を「金科玉条」にする
現実のデータには「クラスタ」が明瞭に存在しないことも多く、 連続的なグラデーションを無理にクラスタ化している場合がある。 ファジークラスタリング(FCM)・ソフト割り当て(GMM)の検討。 「全データを離散的に分ける」前提を疑う。

⚠️ 落とし穴 — 完全強化版

クラスタリング を 47 都道府県 × 複数列 で k-means に適用する際に踏みやすい落とし穴を 5 件挙げます。 47 都道府県という小標本(n=47)に固有の問題が多いため、 先に頭に入れておきましょう。

📝 補足:直感をもう一段掘る(追記)

クラスタリングを一言で言えば 「正解ラベルを一切与えず、 似たサンプル同士を自動でグループにまとめる教師なし学習。 分類(教師あり)が「先生が付けた正解に合わせる」のに対し、 クラスタリングは「データの内部構造をアルゴリズムが自力で発見する」点が本質的に違います。 だからこそ正解がなく、 評価が難しいという宿命を最初から背負います。

手法は「何をもって近い/似ているとするか」で大きく3系統に分かれます。

比喩でいえば、 k-means は「4つの磁石(重心)を置いて鉄粉を引き寄せる」、 階層型は「近いもの同士を手をつながせて家系図を作る」、 DBSCAN は「人が混み合っている場所を1つの群衆とみなし、 ぽつんと立つ人は無視」というイメージ。 同じデータでも、 使う手法・距離・変数が変われば別のグループ分けが出る——「唯一の正しい分類」ではなく「分析者の選択を映した類型化」だ、 というのがクラスタリングを扱う際の最も大切な直感です。

📝 補足:落とし穴を SSDSE-B 実測で確かめる(重要・追記)

本文の落とし穴リストを、 SSDSE-B-2026(2023年・47都道府県)の実測値で「本当にそうなるのか」検証します。 変数は本文と同じ5指標(総人口 A1101・高齢化率・消費支出・人口10万対診療所数・標準地価 C5401)。 数値はすべて実行結果で、 合成データではありません。

① 「標準化を忘れる」と何が起きるか(実測)

標準化ありなしで k-means(k=4)を実行し、 シルエット係数とクラスタサイズを比べます。

前処理シルエットクラスタサイズ実態
標準化あり0.268[13, 1, 8, 25]5変数がほぼ平等に効く
標準化なし(生値)0.735[5, 37, 1, 4]37県が一塊。 実質「総人口・標準地価」だけで分割

ここが罠です。 標準化を忘れた方がシルエットは 0.735 と一見「高品質」に見える。 しかし中身は、 桁の大きい総人口(数百万)と標準地価(数万円)だけで距離が決まり、 高齢化率(20〜35%)や消費支出はほぼ無視され、 37県が1つの塊に潰れた退化した分割にすぎません。 教訓は2つ——(1) 単位の違う変数は必ず 標準化 する、 (2) シルエットが高い=良いクラスタリング、 とは限らない(指標は「分かれ具合」を測るだけで「意味のある分かれ方か」は測らない)。

② 「ランダムでも分かれてしまう」を数値で(実測)

構造を持たない乱数データ($N(0,1)$ の 47×5 行列、 架空データ)を k-means(k=4)で20回クラスタリングした平均シルエットは 0.185(範囲 0.151〜0.229)。 つまりクラスタ構造がゼロでも 0.18 程度は当然出る。 一方、 実データ(47都道府県)の k=4 シルエットは 0.268 で、 乱数ベースラインをわずかに上回る程度。 これは「47県の5指標には強いクラスタ構造は存在せず、 連続的なグラデーションを便宜的に4分割している」ことを示します。 「クラスタが出た=構造がある」ではない好例で、 乱数データとのギャップ(ギャップ統計量の発想)で確かめるのが定石です。

③ 「指標でクラスタ数の最適解が割れる」を数値で(実測)

標準化後 k-means の $k=2\sim6$ を3指標で評価すると、 最適 $k$ が指標ごとに食い違います(シルエット・CHは大きいほど良い、 Davies-Bouldinは小さいほど良い)。

kInertiaシルエットDavies-BouldinCalinski-Harabasz
2153.30.4880.95824.0
3119.50.2481.15421.3
489.10.2680.96923.5
574.30.2720.94222.7
665.40.2031.03021.3

シルエットと Calinski-Harabasz は $k=2$(都市/地方の二分)を最良とし、 Davies-Bouldin は $k=5$ を最良とする。 エルボー(Inertia)は $k=3\sim4$ で緩む。 単一指標を鵜呑みにすると結論が変わるため、 複数指標+解釈しやすさ+事前理論で総合判断するしかありません(本文が $k=4$ を採るのは解釈性重視の実務判断)。

④ 「手法で結果が変わる/どこまで安定か」を数値で(実測)

同じ標準化データ(k=4)で手法を替え、 k-means を基準に一致度(ARI: 調整ランド指数、 1が完全一致)を測りました。

手法シルエットk-means とのARI
k-means0.469
階層・完全連結0.4691.000
階層・Ward法0.4691.000
GMM(混合ガウス)0.3780.637

分割型(k-means)と階層型(完全連結・Ward 法)は ARI = 1.000、 つまり 47 県すべてでまったく同じ 4 分割になりました。 GMM だけは ARI 0.637・シルエット 0.378 とやや乖離します。 GMM は楕円形のクラスタと確率的な所属を許すため、 球状を仮定する k-means とは別の切り方をしたわけです。 複数手法で安定して同じ群が出るかを確認するのが頑健性チェックで、 ここでは「k-means と階層法は信頼できるが、 GMM 解は別途吟味が要る」と読めます。

⑤ 「DBSCAN は eps 一つで激変する」を数値で(実測)

DBSCAN(min_samples=3)の近傍半径 $\varepsilon$ を動かすと、 検出クラスタ数とノイズ点数が大きく振れます。

ε検出クラスタ数ノイズ点数(/47)
1.0117
1.2115
1.525
2.012

$\varepsilon$ を少し変えるだけで「17県がノイズ」から「全県ほぼ1塊」まで結果が一変します。 5次元・47点という疎で小さいデータでは密度差が乏しく DBSCAN が素直に効かないことも分かります。 $\varepsilon$ は勘で決めず、 k距離プロット(k-distance plot)や、 $\varepsilon$ 不要の HDBSCAN を検討するのが定石です。

📝 補足:発展トピックの地図(追記)

クラスタリングは「手法・評価・クラスタ数決定・前処理」の4層で整理すると全体像が掴めます。 各項目は本サイトの個別ページ(存在するもののみリンク)へ接続します。

① 代表的手法とその守備範囲

手法系統強み弱み・仮定
k-means分割・重心高速・大規模向きk指定必須、 球状仮定、 初期値依存
階層Ward等)連結k後決め、 樹形図で構造可視化計算量 $O(n^3)$、 中規模まで
DBSCAN/HDBSCAN密度非球形・ノイズ検出、 k自動ε・MinPts敏感、 密度差に弱い
GMMモデル(確率)楕円形・ソフト所属・重なり許容正規分布仮定、 EMが局所解
スペクトラルグラフ複雑な非線形構造に強い類似度行列の構築が要、 大規模で重い

② 評価指標(内部/外部)

③ クラスタ数 k の決め方

④ 前処理・次元削減・ソフトクラスタリング

📝 リンク方針:本補足のリンクは html/glossary/ 内に実在するページのみに張っています。 シルエット係数・エルボー法・ギャップ統計量は独立ページが未整備のため、 用語のみをテキストで示しています(今後ページが追加されればリンク化予定)。

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

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

📍 体系階層のパス

🌐 統計・データサイエンス教師なし学習クラスタリングクラスタリング

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

中心に クラスタリング を置き、 そこから PCA・因子分析・標準化・共分散・k-means・階層型 など 計 11 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語あとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。

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

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

大きな円が小さな円を包含する Circle Packing 図。 「クラスタリング」は緑色でハイライト

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

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

長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「クラスタリング」は緑色でハイライト

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

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

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

🗺 概念マップ — 完全強化版

🔗 隣接手法への橋渡し

「クラスタリング」は教師なしでデータを群に分ける手法で、 上流の標準化と下流の評価指標 (シルエット・エルボー) を欠くと、 クラスタ数の主観で結論が変わってしまう。

上流の標準化と次元削減で距離を整え、 並列の k-means・階層型・DBSCAN を結果比較し、 下流のシルエット係数とプロット可視化を組み合わせれば、 SSDSE 都道府県を堅牢にライフスタイル群に分けられる。

🌳 手法選択フロー

クラスタリング手法は「クラスタ数 k を事前に決められるか」「クラスタ形状は球形か」「ノイズ点を分離したいか」「所属確率が欲しいか」「データ規模はどれくらいか」の 5 つで選び分ける。 以下に判断フローと典型シナリオを示す。

判断フロー (Yes/No テーブル)

問い Yes なら No なら
クラスタ数 k を事前に決められるかk-means / k-medoids / GMMDBSCAN / HDBSCAN / 階層型
クラスタ形状は球形か (等方分散)k-means / GMMDBSCAN / OPTICS / Mean Shift
階層構造 (デンドログラム) を見たいかWard / 単連結 / 完全連結 / 群平均分割型 / 密度ベースへ
ノイズ点を「どこにも属さない」と判定したいかDBSCAN / HDBSCANk-means など (全点を必ず割当)
所属確率 (重なり) が欲しいかGMM (EM アルゴリズム) / Fuzzy c-meansk-means など (硬い割当)
大規模データ (n > 10 万) かMiniBatchKMeans / BIRCH標準実装でよい

典型シナリオ別の選び方

選んだ後の検証ステップ

  1. 標準化: 変数の単位/分散が違うなら必ず StandardScaler で z-score 化 (これを忘れると距離が桁の大きい変数で支配される)
  2. k の決定: エルボー法 (SSE プロット) + シルエット係数 (>0.5 が良) + Calinski-Harabasz の三点セット
  3. 初期値依存の確認: k-means は n_init=10 以上で複数初期値、 結果が安定するか確認
  4. クラスタの解釈: 各クラスタの変数平均・標準偏差・代表サンプル (中心に近い点) を確認
  5. 外部評価 (正解ラベルあれば): 調整ランド指数 (ARI) / 正規化相互情報量 (NMI) / 純度 (purity)
  6. 頑健性チェック: 別手法 (例: k-means vs Ward) で結果が大きく違わないか ARI で比較

🎮 触って理解する — 同じデータを4手法で分けてみる

クラスタリングの本質は「手法ごとに『クラスタとは何か』の定義(前提)が違う」こと。 下のキャンバスに 2 次元データを用意し(プリセット選択、 またはクリック / タップで点を追加)、 k-means・階層型(Ward / 単連結)・密度ベース(DBSCAN)がそれぞれ同じデータをどうグループ分けするかを、 その場で見比べられます。 計算はすべてブラウザ内で正確に実行(k-means は k-means++ 初期化 × 10 リスタートの最良解、 階層型は凝集型の全ペア距離計算、 DBSCAN は標準アルゴリズム)。 平均シルエット係数クラスタ内二乗和(WSS)もリアルタイムで更新されます。

データ:
手法:
点数: 0 / 検出クラスタ数: / 平均シルエット係数: / WSS (クラスタ内二乗和): / ノイズ点:

キャンバスをクリック / タップすると点を追加できます(最大 300 点)。 大きい × は k-means の重心。 灰色の × は DBSCAN が「どのクラスタにも属さない」と判定したノイズ点(シルエット係数・WSS の計算から除外)。 座標は [0, 1] × [0, 1] に正規化済み。

👀 観察ポイント — 手法の向き不向きを体感する

  1. 「3つの塊」× k-means (k=3):球状の塊は k-means の得意分野。 シルエット係数はおよそ 0.6〜0.7 と高く、 4 手法ともほぼ同じ答えになる。 手法間で結果が一致する = 構造が頑健という証拠。
  2. 「同心円」× k-means:k をいくつにしても、 輪をパイのように放射状に切ってしまい、 内側の輪と外側の輪を分けられない。 k-means は「クラスタ = 重心の周りの球状の塊」と仮定しているため、 重心が同じ位置に重なる同心円は原理的に苦手。
  3. 「同心円」× 階層型 (単連結) or DBSCAN:単連結は「近い点を鎖状につないでいく」、 DBSCAN は「密度が連続する範囲」をクラスタとするので、 2 本の輪を正しく分離できる(DBSCAN は ε ≈ 0.06〜0.08 で試す。 ε = 0.05 まで下げると輪が途切れて細切れになる — パラメータ感度も体感できる)。 同じデータでも前提が合う手法なら解ける。
  4. シルエット係数の罠:同心円を DBSCAN が正しく 2 輪に分けても、 シルエット係数は低い(0 前後〜負)ことを確認。 シルエット係数は「球状にまとまった凸なクラスタ」を前提とした距離ベースの指標なので、 非凸クラスタでは正しい分割でも低スコアになる。 評価指標にも前提がある
  5. 「2本の帯」:細長いクラスタでは k-means / Ward が帯を途中で分断しがち。 単連結は帯に沿ってつなげられるが、 帯同士が 1 点でも橋渡しされると全部つながる(鎖効果)。 点を 1 つ帯の間に追加して壊してみよう。
  6. 「一様ランダム」:構造がないデータでも k-means は必ず k 個に分割し、 それらしい色分けを返す。 しかしシルエット係数は 0.3〜0.4 程度に留まる。 「クラスタが出力された」ことは「クラスタ構造が存在する」ことを意味しない。 本編の落とし穴 ①(クラスタ構造の有無の確認)を体感できる。
  7. k を動かす:「3つの塊」で k = 2〜6 を動かし、 シルエット係数が k=3 で最大になることを確認。 これがシルエット法によるクラスタ数決定の原理(エルボー法は WSS の下がり方の折れ目を見る)。

⚠️ この実験が示す落とし穴(本編の再確認)

🚀 発展 — 評価指標・次元削減との併用