クラスタリング手法・距離・評価指標:
論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:
🍰 まずはやさしく
似たもの同士を分けるグループ分けです。
データの共通点を見つけるために使います。
スマホのアプリを種類別に分けるようなものです。
代表的な分け方や評価の方法を学びます。
🍰 まずはやさしく
正解がないデータを分ける手法です。
データの傾向を分析するために使います。
部活のメンバーを特徴で分けるようなものです。
いくつかの代表的な手法について解説します。
本ページでは、 クラスタリングを統合的に解説します。 k-means・階層クラスタリング・DBSCAN・GMM (混合ガウス)・クラスタ評価を一気通貫で扱います。
クラスタリングは「ラベルなしデータをグループに分ける」教師なし学習の代表手法。 顧客セグメンテーション・異常検知・データ探索の基本ツールです。
🍰 まずはやさしく
似た人を同じテーブルに集める操作です。
データの自然なまとまりを探すために使います。
買い物で似た商品を並べるようなものです。
距離を使って自動で分ける仕組みを学びます。
クラスタリングは「似た者同士をテーブルに集める」操作です。 SSDSE-B-2026 で 47 都道府県を眺めると、 「人口が多く出生率は低い」群(東京 ・ 大阪)と「人口は少ないが高齢化率が高い」群(秋田 ・ 高知)が自然に浮かびます。 k-means はこの「テーブル分け」を距離だけで自動化するシンプルな比喩です。
$$ J = \sum_{k=1}^{K} \sum_{x_i \in C_k} \| x_i - \mu_k \|^2 $$
クラスタリングはアルゴリズムを動かすだけではなく、「何のために、 どの距離尺度で、 どの変数を使い、 結果をどう検証するか」という設計が品質を左右します。 ここでは SSDSE-B-2026(47 都道府県)を題材に、 散布図・ヒストグラム・箱ひげ図の 3 視点でクラスタ構造を読み解き、 さらに比較・落とし穴・実務応用を詰めていきます。
log(人口) と 高齢化率 で散布したイメージ。 右下に「人口大・高齢化低」(東京・大阪・神奈川)、 左上に「人口小・高齢化高」(秋田・高知・島根)、 中央に「中規模・中位高齢化」(広島・宮城・新潟)が分布する。散布図はクラスタリング前の必須ステップです。 「クラスタが視覚的にいくつ見えるか」を肉眼で確認し、 アルゴリズムに対する事前期待値を設定します。 k-means の初期値依存性や DBSCAN の半径選択は、 この散布図を見ながら粗く当たりをつけることで大きく安定します。
総人口 のヒストグラム。 右裾が極端に長く、 東京・神奈川・大阪・愛知が外れ値的に振る舞う。 そのままユークリッド距離を使うと、 これら 4 都府県が「自分だけのクラスタ」を独占しがちで、 残り 43 県の構造が潰れる。ヒストグラムが強く右に歪んでいる場合、 標準化だけでは外れ値の影響を抑えきれません。 対数変換(np.log1p)や Box-Cox 変換、 あるいはロバスト距離(マンハッタン距離・マハラノビス距離)の検討が必要です。 「クラスタリング前にヒスト 1 枚を描く」だけで、 後段の解釈が大幅に楽になります。
一般診療所数(I5102) 箱ひげ図。 東京(単独群, 中央値≈14,900)が突出し、 大都市圏群(北海道・神奈川・大阪・愛知・福岡など, ≈5,000)が続き、 中位地方群(宮城・静岡・広島など, ≈1,500)は中位、 小規模・過疎群(秋田・鳥取・高知など, ≈900)は下方に詰まる。 中央値・四分位範囲・外れ値の 3 視点で「クラスタが意味ある分割か」を一目で検証できる。クラスタリングは「変数の平均ベクトル」だけで評価されがちですが、 箱ひげ図で 四分位範囲(IQR)と外れ値の分布 まで観察すると、 そのクラスタが「均質な集まり」なのか「平均が近いだけで分散が大きい寄せ集め」なのかを判別できます。 後者は再分割(K を増やす)か、 ロバスト法(k-medoids、 GMM の covariance_type='full')への切替を検討します。
🍰 まずはやさしく
データの「近さ」を測るためのルールです。
正しくグループ分けするために使います。
テストの点数など単位を揃える作業に似ています。
距離の選び方と前処理について解説します。
| 距離 | 数式(概略) | 向いている場面 | SSDSE-B での具体例 |
|---|---|---|---|
| ユークリッド | $\sqrt{\sum (x_i-y_i)^2}$ | 連続変数・標準化済 | 標準化後の人口・所得 |
| マンハッタン | $\sum |x_i-y_i|$ | 外れ値が多い | 小売販売額のような歪み |
| マハラノビス | $\sqrt{(x-y)^\top \Sigma^{-1} (x-y)}$ | 変数間に相関がある | 人口と病院数(相関0.9超) |
| コサイン | $1 - \frac{x \cdot y}{\|x\|\|y\|}$ | プロファイル形状を比較 | 産業構成比(合計100%) |
| ハミング | $\#\{i: x_i \neq y_i\}$ | カテゴリ変数 | 地域区分・気候帯 |
SSDSE-B-2026 は単位がバラバラ(円・人・%・件)なので、 距離計算前の標準化(StandardScaler)か対数化が必須です。 標準化を忘れると「製造品出荷額等(兆円オーダー)」だけで距離が決まり、 他の変数が無視されます。 これは初学者が最も多くハマる罠です。
| 前処理 | 効用 | 注意点 | 推奨条件 |
|---|---|---|---|
| StandardScaler | 平均0・分散1で揃える | 外れ値に弱い | 正規分布に近い |
| RobustScaler | 中央値・IQRで揃える | 分布の裾は残る | 外れ値多め |
| log1p | 右裾を圧縮 | 負値・ゼロ含み禁止 | 人口・金額 |
| PCA(事前圧縮) | 多重共線性を低減 | 解釈が落ちる | 変数 20+ の高次元 |
| Yeo-Johnson | 正規化と歪み低減 | 解釈が複雑 | 混合(正負)データ |
このコードでやること: SSDSE-B-2026 の都道府県データに StandardScaler と RobustScaler を適用し、 k-means の結果がどう変わるかをシルエット係数で比較する。
📥 入力データ(SSDSE-B-2026 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pandas as pd from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler, RobustScaler from sklearn.metrics import silhouette_score df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) df.columns = [c.strip() for c in df.columns] feats = ['総人口', '製造品出荷額等', '小売業年間商品販売額', '65歳以上人口'] X = df[feats].astype(float) for name, scaler in [('Standard', StandardScaler()), ('Robust', RobustScaler())]: Xs = scaler.fit_transform(X) km = KMeans(n_clusters=4, n_init=20, random_state=0).fit(Xs) sil = silhouette_score(Xs, km.labels_) print(f'{name:8s} シルエット = {sil:.3f}') |
📤 実行結果(典型例):
💬 解釈: RobustScaler の方がシルエットが高い(0.486 > 0.412)。 SSDSE-B の人口・金額は東京を中心に外れ値が大きいため、 中央値ベースの RobustScaler が安定して群を分けられている。 「変数の分布を確認 → 適切なスケーラー選択 → シルエットで比較」の 3 ステップは、 すべてのクラスタリングプロジェクトで実施すべきルーチンです。
「K をいくつにすれば良いか」はクラスタリングで最も論争の多い問いです。 単一の万能法則はなく、 複数指標を組み合わせて意思決定するのが定石です。
| 手法 | 指標 | 読み方 | 長所 | 短所 |
|---|---|---|---|---|
| エルボー法 | WCSS | 曲がり角 | 直感的 | 曖昧 |
| シルエット係数 | [-1, 1] | 最大化 | 定量的 | 非凸群に弱い |
| Calinski-Harabasz | $\ge 0$ | 最大化 | 高速 | K大で過大評価 |
| Davies-Bouldin | $\ge 0$ | 最小化 | 分離度を直接評価 | 球状仮定 |
| Gap 統計量 | 差分 | 最大化 | 理論的根拠 | 計算コスト高 |
このコードでやること: SSDSE-B-2026 で K=2〜10 を試し、 シルエット・Calinski-Harabasz・Davies-Bouldin の 3 指標を並べて意思決定する。
📥 入力: 標準化済みの 47 都道府県 × 8 特徴量 ndarray X(前述コードで作成)
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 | import pandas as pd # ── この抜粋だけで動くように、47 都道府県の特徴量を用意する ── # 英字の項目コードを使うので、2 行目の日本語名は読み飛ばす _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) _d = _d[_d['SSDSE-B-2026'] == 2023] # 最新年度の 47 行 _cols = ['A1101', 'A1301', 'A1303', 'A4101', 'L3221'] # 総人口・15歳未満・65歳以上・出生数・消費支出 from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans # クラスタリングは距離で測るので、必ず標準化してから使う Xs = StandardScaler().fit_transform(_d[_cols].astype(float).values) from sklearn.metrics import (silhouette_score, calinski_harabasz_score, davies_bouldin_score) rows = [] for k in range(2, 11): km = KMeans(n_clusters=k, n_init=20, random_state=0).fit(Xs) rows.append({ 'K': k, 'WCSS': round(km.inertia_, 1), 'Silhouette': round(silhouette_score(Xs, km.labels_), 3), 'CH': round(calinski_harabasz_score(Xs, km.labels_), 1), 'DB': round(davies_bouldin_score(Xs, km.labels_), 3), }) print(pd.DataFrame(rows).to_string(index=False)) |
📤 実行結果(典型例):
💬 解釈: シルエットは K=2 と K=4 で 0.48 台のピーク。 Calinski-Harabasz は K=4 で最大、 Davies-Bouldin も K=4 で最小(0.78)。 3 指標が一致して K=4 を推奨。 「3 指標一致のときは強い証拠」というのが実務でのコンセンサスです。 K=2 のシルエットが高いのは「東京を含む大都市群 vs その他」という単純構造の反映で、 解釈上は K=4 の「大都市 / 工業県 / 中規模 / 地方」が情報量で勝ります。
| 指標 | 定義 | 用途 | 参考閾値 |
|---|---|---|---|
| Dunn 指数 | クラスタ間最小距離 / クラスタ内最大直径 | 「離れて締まっている」評価 | 高いほど良 |
| Xie-Beni | 圧縮度 / 分離度 | FCM 系で頻用 | 低いほど良 |
| BIC | 尤度ペナルティ付き | GMM の K 決定 | 最小化 |
| Stability | ブートストラップ一致率 | 再現性評価 | >0.85 が目安 |
階層クラスタリングは「最も近い 2 群を融合」を繰り返してデンドログラム(樹形図)を作ります。 ところが「2 群間の距離」をどう測るかで結果が大きく変わります。 代表的なのが 単連結(最短距離)・完全連結(最長距離)・平均連結・Ward 法 の 4 種類です。
| リンケージ | クラスタ間距離 | 傾向 | 弱点 | SSDSE-B での挙動 |
|---|---|---|---|---|
| 単連結(single) | $\min_{x \in A, y \in B} d(x,y)$ | 細長い帯状群が得意 | 連鎖(chaining)効果 | 島嶼県が連なって 1 つに |
| 完全連結(complete) | $\max_{x \in A, y \in B} d(x,y)$ | 球状でバランス良 | 外れ値に過敏 | 東京が単独群 |
| 平均連結(average) | $\frac{1}{|A||B|}\sum d(x,y)$ | 中庸でロバスト | 理論的根拠が弱め | 4 群を均衡分割 |
| Ward 法 | 融合後分散の増分 | k-means と整合 | ユークリッド限定 | 最も解釈しやすい |
このコードでやること: SSDSE-B-2026 を 4 種類のリンケージで分け、 デンドログラムを描画して比較する。 Ward 法と完全連結がどれだけ違うかを実感する。
📥 入力: 標準化済み Xs と 47 都道府県名リスト names
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd # df / Xs はこのあとのブロックで作っているので、ここでも用意しておく df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) df = df[df['年度'] == df['年度'].max()].reset_index(drop=True) from scipy.cluster.hierarchy import linkage, fcluster, dendrogram from sklearn.metrics import silhouette_score names = df['都道府県'].tolist() results = [] for method in ['single', 'complete', 'average', 'ward']: Z = linkage(Xs, method=method) labels = fcluster(Z, t=4, criterion='maxclust') sil = silhouette_score(Xs, labels) sizes = pd.Series(labels).value_counts().sort_index().tolist() results.append({'method': method, 'silhouette': round(sil, 3), 'cluster_sizes': sizes}) print(pd.DataFrame(results).to_string(index=False)) |
📤 実行結果(典型例):
💬 解釈: 単連結はシルエット 0.582 と高いが、 サイズ [44, 1, 1, 1] という極端な不均衡(東京・大阪・神奈川が単独群)が出ており、 「シルエットだけで判断するな」という典型例。 Ward 法は [18, 15, 11, 3] でバランスが取れ、 大都市 3 県(東京・神奈川・大阪)も独立群として確保している。 SSDSE-B では Ward 法が解釈と均衡の両立点。
Ward 法はクラスタ $A$ と $B$ を融合したときの「グループ内偏差二乗和の増分」を距離とします。
$$ d_{Ward}(A, B) = \frac{|A| \cdot |B|}{|A| + |B|} \cdot \| \bar{x}_A - \bar{x}_B \|^2 $$
この式は「群を融合すると、 重心からの偏差がどれだけ増えるか」を最小化する選択を毎ステップ行うことを意味します。 結果として k-means と同じ「群内分散最小化」基準 が階層的に達成されるため、 K を決めた後の整合性が高くなります。
k-means や Ward 法は 球状クラスタ仮定 を持ちます。 三日月型や同心円型のデータでは大失敗します。 DBSCAN(Density-Based Spatial Clustering of Applications with Noise)は「密度が一定以上の領域を 1 群と見なす」発想で、 任意形状とノイズ点の同時検出を可能にします。
| パラメータ | 意味 | 大きくすると | 小さくすると | SSDSE-B 推奨値 |
|---|---|---|---|---|
| eps | 近傍半径 | 大きな群が形成 | ノイズ多 | 1.2〜1.8(標準化後) |
| min_samples | コア点最小近傍数 | 厳格・群減少 | 小群・ノイズ減 | 3〜5(47 サンプル) |
このコードでやること: SSDSE-B-2026 に DBSCAN を適用し、 eps を変えると群数とノイズ数がどう変化するかを観察する。 k-distance プロットで eps の目安も導出する。
📥 入力: 標準化済み Xs(前述)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from sklearn.cluster import DBSCAN from sklearn.neighbors import NearestNeighbors # 1) k-distance グラフで eps の目安を見る(k=4: min_samples-1 相当) nn = NearestNeighbors(n_neighbors=4).fit(Xs) dists, _ = nn.kneighbors(Xs) kd = sorted(dists[:, -1]) print('4-distance 中央値:', round(kd[len(kd)//2], 3), '/ 上位 90%点:', round(kd[int(len(kd)*0.9)], 3)) # 2) eps スイープ for eps in [1.0, 1.4, 1.8, 2.2]: db = DBSCAN(eps=eps, min_samples=4).fit(Xs) n_clusters = len(set(db.labels_)) - (1 if -1 in db.labels_ else 0) n_noise = list(db.labels_).count(-1) print(f'eps={eps:.1f}: クラスタ数={n_clusters}, ノイズ点={n_noise}') |
📤 実行結果(典型例):
💬 解釈: 4-distance の中央値 1.48 は eps の良い初期値で、 実際 eps=1.4 で 3 群+ノイズ 11 件が得られる。 eps=1.8 まで広げるとノイズが減るが群が 2 つに統合される。 「ノイズが情報を持つ」のが DBSCAN の真価で、 「k-means でなら強制的に最寄りクラスタに押し込まれる外れ値」を「ノイズ群(label=-1)」として明示的に区別できる点が k-means との決定的な違いです。
HDBSCAN は eps を不要にし、 「密度の階層」から自動的に最適なクラスタを抽出します。 SSDSE-B のように サンプル数が少ない(47 件)かつ密度差が大きい データでは、 DBSCAN より HDBSCAN の方が安定するケースが多いです。 min_cluster_size=3 程度に設定し、 cluster_persistence_ 属性で「群の安定性」を確認します。
GMM はサンプルが 複数のガウス分布の混合 から生成されたと仮定し、 EM アルゴリズムで各クラスタの平均・共分散・重みを推定します。 出力は「ハードラベル」ではなく 各群への所属確率 なので、 「東京は『大都市群 90%・工業県群 10%』のように混ざっている」という柔軟な解釈が可能です。
| covariance_type | パラメータ数 | 形状 | 推奨条件 |
|---|---|---|---|
| spherical | K | 球(k-means 等価) | 変数間独立・等分散 |
| diag | K × D | 軸方向楕円 | 変数間独立・異分散 |
| tied | D × D | 全群同共分散 | サンプル少 |
| full | K × D × D | 任意楕円 | サンプル多・柔軟性必要 |
このコードでやること: SSDSE-B-2026 に GMM を適用し、 BIC で最適 K を選び、 所属確率を可視化する。
📥 入力: 標準化済み Xs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from sklearn.mixture import GaussianMixture bic_table = [] for k in range(2, 8): for cov in ['spherical', 'diag', 'tied', 'full']: gm = GaussianMixture(n_components=k, covariance_type=cov, n_init=10, random_state=0).fit(Xs) bic_table.append({'K': k, 'cov': cov, 'BIC': round(gm.bic(Xs), 1), 'logL': round(gm.score(Xs) * len(Xs), 1)}) bic_df = pd.DataFrame(bic_table).pivot(index='K', columns='cov', values='BIC') print(bic_df) print('\nBIC 最小:', bic_df.stack().idxmin(), '=', round(bic_df.stack().min(), 1)) |
📤 実行結果(典型例):
💬 解釈: BIC は K=4・spherical で最小(784.2)。 spherical はパラメータが少なく過学習に強いため、 47 サンプルの SSDSE-B では合理的な選択。 サンプル数 ÷ パラメータ数 ≥ 10 を満たすかを必ず確認してください。 full 共分散は柔軟性が高い分、 4 群 × 8 変数で K×D(D+1)/2 = 144 パラメータ、 47 サンプルでは推定が不安定です。
$$ \text{BIC} = -2 \log L + p \log n, \quad \text{AIC} = -2 \log L + 2p $$
BIC は シンプルさを強く好み、 AIC は 予測精度を好み ます。 サンプル数 $n$ が大きいほど BIC のペナルティが厳しく、 「真のモデルが候補に含まれる」状況では BIC が一致性を持ちます。 SSDSE-B のように小サンプルでは BIC が無難ですが、 「群を多めに切ってマーケティングセグメントとして使う」目的なら AIC や Cross-validation 尤度の方が実用的です。
| 分類 | 指標 | 必要なもの | 解釈 |
|---|---|---|---|
| 内部 | シルエット | 特徴量 | [-1, 1] 高いほど良 |
| 内部 | Calinski-Harabasz | 特徴量 | 高いほど良・K大で過大 |
| 内部 | Davies-Bouldin | 特徴量 | 低いほど良 |
| 外部 | ARI | 正解ラベル | [-1, 1] 1 が完全一致 |
| 外部 | NMI | 正解ラベル | [0, 1] 1 が完全一致 |
| 外部 | Homogeneity / Completeness | 正解ラベル | [0, 1] 1 が完全 |
| 安定性 | Bootstrap ARI | 再サンプリング | >0.85 が安定の目安 |
このコードでやること: SSDSE-B-2026 で k-means と Ward 法のクラスタを比較し、 ARI と NMI を計算。 さらに 100 回ブートストラップして再現性を測定する。
📥 入力: 標準化済み Xs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from sklearn.cluster import KMeans, AgglomerativeClustering from sklearn.metrics import adjusted_rand_score, normalized_mutual_info_score km = KMeans(n_clusters=4, n_init=20, random_state=0).fit(Xs) ag = AgglomerativeClustering(n_clusters=4, linkage='ward').fit(Xs) print('k-means vs Ward') print(' ARI =', round(adjusted_rand_score(km.labels_, ag.labels_), 3)) print(' NMI =', round(normalized_mutual_info_score(km.labels_, ag.labels_), 3)) # ブートストラップ安定性 import numpy as np rng = np.random.RandomState(0) n = len(Xs) aris = [] for _ in range(100): idx = rng.choice(n, n, replace=True) km_b = KMeans(n_clusters=4, n_init=10, random_state=1).fit(Xs[idx]) # 元データに predict(k-means は predict 可能) full_lab = km.labels_ boot_lab = km_b.predict(Xs) aris.append(adjusted_rand_score(full_lab, boot_lab)) print(f'Bootstrap ARI 平均 = {np.mean(aris):.3f} ± {np.std(aris):.3f}') |
📤 実行結果(典型例):
💬 解釈: ARI = 0.81 / NMI = 0.85 で k-means と Ward 法が高い一致を示している。 さらにブートストラップ ARI 平均 0.891 は 0.85 を超えており「安定」 と判定可能。 「異なる手法で同じ結果 + 再サンプリングでも同じ結果」を確認できれば、 クラスタ解釈の信頼度が大きく上がります。
前節の数式に含まれる記号を、 日本語の意味に翻訳する。
clustering group の数式は、 これらの記号を組み合わせて「データから未知量を推定する」あるいは「データの構造を要約する」プロセスを記述している。
SSDSE-B-2026 の経済・人口・社会指標(標準化後 8 変数)を k-means、 Ward 階層、 GMM で分け、 シルエットで比較する。
📥 入力例: data/raw/SSDSE-B-2026.csv 特徴量: A1101(総人口)・A1303(65歳以上人口)・L3221(消費支出)・I5102(一般診療所数)・G7101(延べ宿泊者数)・A4101(出生数)・A9101(婚姻件数)・E2101(小学校数) 前処理: StandardScaler で平均 0、 分散 1 に標準化
📤 実行例: KMeans(n_clusters=4, n_init=20, random_state=0).fit(X) 各クラスタの代表都道府県: C0: 宮城・静岡・広島 ほか22県(中位地方群) C1: 神奈川・大阪・愛知・福岡 ほか8県(大都市圏) C2: 東京(単独・突出) C3: 秋田・鳥取・高知 ほか16県(小規模・過疎群) シルエット係数 = 0.382(k=4, 中程度の分離)
💬 読み方: k-means は球状クラスタに強いが、 非球状や密度異質には弱い。 実測ではシルエット係数は K=2 の 0.647 が最大で、 K=3 で 0.453、 K=4 で 0.457、 K=6 では 0.372 まで下がる。 つまり K を増やせば良くなるわけではない ── WCSS のエルボー・CH・DB もあわせて適切な K を選ぶ。 都道府県の 4 類型化は社会保障・地方創生政策の議論に使える。
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 | import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN from sklearn.mixture import GaussianMixture from sklearn.preprocessing import StandardScaler from sklearn.metrics import (silhouette_score, calinski_harabasz_score, davies_bouldin_score) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026']==2023].copy() feats = ['A1101', 'A1303', 'L3221', 'I5102', 'G7101', 'A4101', 'A9101', 'E2101'] X = StandardScaler().fit_transform(df[feats].astype(float)) methods = { 'KMeans(k=4)' : KMeans(n_clusters=4, n_init=20, random_state=0), 'KMeans(k=5)' : KMeans(n_clusters=5, n_init=20, random_state=0), 'Agglo Ward(k=4)' : AgglomerativeClustering(n_clusters=4, linkage='ward'), 'GaussianMixture(k=4)': GaussianMixture(n_components=4, random_state=0), } for name, m in methods.items(): labels = m.fit_predict(X) sil = silhouette_score(X, labels) ch = calinski_harabasz_score(X, labels) db = davies_bouldin_score(X, labels) print(f'{name:<22s} silhouette={sil:.3f} CH={ch:6.1f} DB={db:.3f}') |
典型的な観察例(random_state=0, 8 変数): k=4 でシルエット ≈ 0.38、 k=5 で 0.36。 k=4 がやや優位で、 「東京(単独・突出)」「大都市圏(神奈川・大阪・愛知・福岡・北海道・埼玉・千葉・兵庫の 8 都道府県)」「中位地方群(宮城・静岡・広島など 22 県)」「小規模・過疎群(秋田・鳥取・高知など 16 県)」という 4 類型に分かれる。 k-means では東京が単独クラスタになり、 GMM だとシルエットが 0.29 に下がって「東京」の切り出し方が変わるなど、 解釈は手法依存。 評価指標は 1 つに頼らず、 シルエット・CH・DB の 3 つを並列で見る。
📥 入力例: data/raw/SSDSE-B-2026.csv 特徴量: A1101(総人口)・A1303(65歳以上人口)・L3221(消費支出)・I5102(一般診療所数)・G7101(延べ宿泊者数)・A4101(出生数)・A9101(婚姻件数)・E2101(小学校数) 前処理: StandardScaler で平均 0、 分散 1 に標準化
📤 実行例: KMeans(n_clusters=4, n_init=20, random_state=0).fit(X) 各クラスタの代表都道府県: C0: 宮城・静岡・広島 ほか22県(中位地方群) C1: 神奈川・大阪・愛知・福岡 ほか8県(大都市圏) C2: 東京(単独・突出) C3: 秋田・鳥取・高知 ほか16県(小規模・過疎群) シルエット係数 = 0.382(k=4, 中程度の分離)
1 2 3 4 5 6 7 8 9 10 11 12 | ks = range(2, 11) inertia, sils = [], [] for k in ks: km = KMeans(n_clusters=k, n_init=20, random_state=0).fit(X) inertia.append(km.inertia_) sils.append(silhouette_score(X, km.labels_)) fig, ax = plt.subplots(1, 2, figsize=(11, 4.5)) ax[0].plot(ks, inertia, 'o-'); ax[0].set_title('Elbow(SSE)') ax[1].plot(ks, sils, 's-', color='orange'); ax[1].set_title('Silhouette') for a in ax: a.set_xlabel('k'); a.grid(alpha=.3) plt.tight_layout(); plt.savefig('elbow_silhouette.png', dpi=140) |
📥 入力例: data/raw/SSDSE-B-2026.csv 特徴量: A1101(総人口)・A1303(65歳以上人口)・L3221(消費支出)・I5102(一般診療所数)・G7101(延べ宿泊者数)・A4101(出生数)・A9101(婚姻件数)・E2101(小学校数) 前処理: StandardScaler で平均 0、 分散 1 に標準化
📤 実行例: KMeans(n_clusters=4, n_init=20, random_state=0).fit(X) 各クラスタの代表都道府県: C0: 宮城・静岡・広島 ほか22県(中位地方群) C1: 神奈川・大阪・愛知・福岡 ほか8県(大都市圏) C2: 東京(単独・突出) C3: 秋田・鳥取・高知 ほか16県(小規模・過疎群) シルエット係数 = 0.382(k=4, 中程度の分離)
1 2 3 4 5 6 7 8 9 | from scipy.cluster.hierarchy import linkage, dendrogram, fcluster Z = linkage(X, method='ward') fig, ax = plt.subplots(figsize=(13, 5)) dendrogram(Z, labels=df['Prefecture'].tolist(), leaf_font_size=8, ax=ax) ax.axhline(8, ls='--', color='red') # k=4 になる切り高さ plt.tight_layout(); plt.savefig('dendrogram.png', dpi=140) labels = fcluster(Z, t=4, criterion='maxclust') df['cluster_ward'] = labels print(df.groupby('cluster_ward')['Prefecture'].apply(list)) |
合成 2D データ 5 点を 2 クラスタに分けたときの重心を更新する。
| i | x | y | クラスタ |
|---|---|---|---|
| 1 | 1 | 2 | A |
| 2 | 2 | 3 | A |
| 3 | 3 | 2 | A |
| 4 | 8 | 9 | B |
| 5 | 9 | 8 | B |
1 2 3 4 5 | import numpy as np A = np.array([[1,2],[2,3],[3,2]]) B = np.array([[8,9],[9,8]]) print(f"A 重心: {A.mean(axis=0)}") print(f"B 重心: {B.mean(axis=0)}") |
💬 手計算 (Step 2) (2.0, 2.33) / (8.5, 8.5) と Python 出力が完全一致。
| 手法 | クラス・関数 |
|---|---|
| k-means | sklearn.cluster.KMeans / MiniBatchKMeans |
| 階層 | scipy.cluster.hierarchy.linkage / dendrogram / fcluster または sklearn.cluster.AgglomerativeClustering |
| DBSCAN | sklearn.cluster.DBSCAN / HDBSCAN |
| GMM | sklearn.mixture.GaussianMixture / BayesianGaussianMixture |
| スペクトラル | sklearn.cluster.SpectralClustering |
| アフィニティ伝播 | sklearn.cluster.AffinityPropagation |
| Mean Shift | sklearn.cluster.MeanShift |
| シルエット | sklearn.metrics.silhouette_score |
| ARI / NMI | sklearn.metrics.adjusted_rand_score / normalized_mutual_info_score |
📥 入力例: data/raw/SSDSE-B-2026.csv 特徴量: A1101(総人口)・A1303(65歳以上人口)・L3221(消費支出)・I5102(一般診療所数)・G7101(延べ宿泊者数)・A4101(出生数)・A9101(婚姻件数)・E2101(小学校数) 前処理: StandardScaler で平均 0、 分散 1 に標準化
📤 実行例: KMeans(n_clusters=4, n_init=20, random_state=0).fit(X) 各クラスタの代表都道府県: C0: 宮城・静岡・広島 ほか22県(中位地方群) C1: 神奈川・大阪・愛知・福岡 ほか8県(大都市圏) C2: 東京(単独・突出) C3: 秋田・鳥取・高知 ほか16県(小規模・過疎群) シルエット係数 = 0.382(k=4, 中程度の分離)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from sklearn.cluster import (KMeans, AgglomerativeClustering, DBSCAN, SpectralClustering, MeanShift) from sklearn.mixture import GaussianMixture models = { 'KMeans' : KMeans(n_clusters=4, n_init=20, random_state=0), 'Agglo (ward)' : AgglomerativeClustering(n_clusters=4, linkage='ward'), 'Agglo (average)': AgglomerativeClustering(n_clusters=4, linkage='average'), 'DBSCAN' : DBSCAN(eps=1.2, min_samples=3), 'Spectral' : SpectralClustering(n_clusters=4, affinity='rbf', random_state=0), 'GMM' : GaussianMixture(n_components=4, random_state=0), 'MeanShift' : MeanShift(), } labels_dict = {n: m.fit_predict(X) for n, m in models.items()} |
📥 入力例: data/raw/SSDSE-B-2026.csv 特徴量: A1101(総人口)・A1303(65歳以上人口)・L3221(消費支出)・I5102(一般診療所数)・G7101(延べ宿泊者数)・A4101(出生数)・A9101(婚姻件数)・E2101(小学校数) 前処理: StandardScaler で平均 0、 分散 1 に標準化
📤 実行例: KMeans(n_clusters=4, n_init=20, random_state=0).fit(X) 各クラスタの代表都道府県: C0: 宮城・静岡・広島 ほか22県(中位地方群) C1: 神奈川・大阪・愛知・福岡 ほか8県(大都市圏) C2: 東京(単独・突出) C3: 秋田・鳥取・高知 ほか16県(小規模・過疎群) シルエット係数 = 0.382(k=4, 中程度の分離)
1 2 3 4 5 6 7 | from scipy.cluster.hierarchy import linkage, dendrogram, fcluster from scipy.spatial.distance import pdist D = pdist(X, metric='euclidean') Z_ward = linkage(D, method='ward') Z_complete = linkage(D, method='complete') Z_average = linkage(D, method='average') labels = fcluster(Z_ward, t=4, criterion='maxclust') |
📥 入力例: data/raw/SSDSE-B-2026.csv 特徴量: A1101(総人口)・A1303(65歳以上人口)・L3221(消費支出)・I5102(一般診療所数)・G7101(延べ宿泊者数)・A4101(出生数)・A9101(婚姻件数)・E2101(小学校数) 前処理: StandardScaler で平均 0、 分散 1 に標準化
📤 実行例: KMeans(n_clusters=4, n_init=20, random_state=0).fit(X) 各クラスタの代表都道府県: C0: 宮城・静岡・広島 ほか22県(中位地方群) C1: 神奈川・大阪・愛知・福岡 ほか8県(大都市圏) C2: 東京(単独・突出) C3: 秋田・鳥取・高知 ほか16県(小規模・過疎群) シルエット係数 = 0.382(k=4, 中程度の分離)
1 2 3 4 5 6 | import hdbscan clusterer = hdbscan.HDBSCAN(min_cluster_size=4, min_samples=2) labels = clusterer.fit_predict(X) print('クラスタ数:', len(set(labels)) - (1 if -1 in labels else 0)) print('ノイズ点 :', np.sum(labels == -1)) print('安定性 :', clusterer.cluster_persistence_) |
📥 入力例: data/raw/SSDSE-B-2026.csv 特徴量: A1101(総人口)・A1303(65歳以上人口)・L3221(消費支出)・I5102(一般診療所数)・G7101(延べ宿泊者数)・A4101(出生数)・A9101(婚姻件数)・E2101(小学校数) 前処理: StandardScaler で平均 0、 分散 1 に標準化
📤 実行例: KMeans(n_clusters=4, n_init=20, random_state=0).fit(X) 各クラスタの代表都道府県: C0: 宮城・静岡・広島 ほか22県(中位地方群) C1: 神奈川・大阪・愛知・福岡 ほか8県(大都市圏) C2: 東京(単独・突出) C3: 秋田・鳥取・高知 ほか16県(小規模・過疎群) シルエット係数 = 0.382(k=4, 中程度の分離)
1 2 3 4 5 6 7 8 | from sklearn.metrics import (silhouette_score, calinski_harabasz_score, davies_bouldin_score, adjusted_rand_score) for name, lab in labels_dict.items(): valid = lab != -1 # DBSCAN のノイズを除外 if len(set(lab[valid])) < 2: continue print(f'{name:<18s} sil={silhouette_score(X[valid], lab[valid]):.3f} ' f'CH={calinski_harabasz_score(X[valid], lab[valid]):.1f} ' f'DB={davies_bouldin_score(X[valid], lab[valid]):.3f}') |
📥 入力例: data/raw/SSDSE-B-2026.csv 特徴量: A1101(総人口)・A1303(65歳以上人口)・L3221(消費支出)・I5102(一般診療所数)・G7101(延べ宿泊者数)・A4101(出生数)・A9101(婚姻件数)・E2101(小学校数) 前処理: StandardScaler で平均 0、 分散 1 に標準化
📤 実行例: KMeans(n_clusters=4, n_init=20, random_state=0).fit(X) 各クラスタの代表都道府県: C0: 宮城・静岡・広島 ほか22県(中位地方群) C1: 神奈川・大阪・愛知・福岡 ほか8県(大都市圏) C2: 東京(単独・突出) C3: 秋田・鳥取・高知 ほか16県(小規模・過疎群) シルエット係数 = 0.382(k=4, 中程度の分離)
1 2 3 4 5 6 7 8 9 10 11 12 13 | from sklearn.decomposition import PCA from sklearn.manifold import TSNE import umap emb_pca = PCA(n_components=2).fit_transform(X) emb_tsne = TSNE(n_components=2, perplexity=8, random_state=0).fit_transform(X) emb_umap = umap.UMAP(n_neighbors=8, random_state=0).fit_transform(X) fig, axes = plt.subplots(1, 3, figsize=(15, 4.5)) for ax, emb, t in zip(axes, [emb_pca, emb_tsne, emb_umap], ['PCA','t-SNE','UMAP']): ax.scatter(emb[:,0], emb[:,1], c=labels_dict['KMeans'], cmap='Set2', s=50) ax.set_title(t) |
このページの主役は個々のアルゴリズムではなく「どの手法を選ぶか」です。同じデータでも手法が変わると結果はまったく変わります。ここでは 4 つのデータ形状 × 4 つの手法(k-means / 階層 Ward / DBSCAN / GMM)を全組み合わせで試し、「手法が仮定するクラスタ像」とデータ形状のマッチングを体感してください。キャンバスをドラッグ(スマホは指でなぞる)と自分で点を描き足して実験することもできます。
✏️ キャンバス上をドラッグ/なぞると点を追加できます(指を離すと再クラスタリング)
※ シルエット係数は「凸(塊状)クラスタ」を暗黙に仮定した指標です。同心円を正しく分けた DBSCAN のシルエットが低く出ることに注目——手法だけでなく評価指標にも「得意な形」があり、数値 1 つで優劣を断定できません。
| データ形状\手法 | k-means | 階層(Ward) | DBSCAN | GMM |
|---|---|---|---|---|
| 🟢 球状の塊 | ◎ | ◎ | ○ | ◎ |
| 🎯 同心円 | ✕ | ✕ | ◎ | ✕ |
| 📏 細長い帯 | △ | △ | ○ | ◎ |
| 🌫 密度差あり | △ | ○ | ✕ | ○ |
どのクラスタリング手法も、内部に「クラスタとはこういう形のはずだ」という仮定(帰納バイアス)を持っています。k-means は「等方的な球」、階層クラスタリングの Ward 法は「コンパクトな塊」、DBSCAN は「密度で繋がった任意形状+ノイズ」、GMM は「傾いた楕円(ガウス分布)」です。手法選択とは、この仮定とデータの実際の形を照合する作業に他なりません。だからこそ、クラスタリングの第一歩はアルゴリズム実行ではなく「散布図(高次元なら次元削減後)を眺めて形を推測すること」なのです。上の実験で同心円に k-means を当てると輪が真っ二つになるのは、k-means が悪いのではなく仮定が合っていないだけ——道具の選び間違いです。
実務の選択フロー:① データを 2 次元に落として散布図を描く → ② 形が見えるなら上のマトリクスで手法を選ぶ(球状なら k-means、輪や三日月なら DBSCAN やスペクトラルクラスタリング、傾いた楕円なら GMM、密度差が大きいなら HDBSCAN) → ③ 形が見えないなら複数手法 × 複数 k を試し、結果同士の一致度(ARI)や解釈可能性で絞り込む、の 3 段階が定石です。
アンサンブル(コンセンサス)クラスタリング:単一手法の仮定に依存しないための発展形として、複数手法・複数初期値・複数 k の結果を「点 i と点 j が同じクラスタに入った回数」の共起行列にまとめ、それを類似度として再クラスタリングする方法があります。個々の手法の癖が平均化され、どの実行でも一緒になるペアだけが安定したクラスタ核として残ります。教師あり学習のアンサンブル学習(バギング等)と同じ発想を教師なし学習に持ち込んだものです。個別手法の詳細はk-means・階層クラスタリング・DBSCAN・GMM・クラスタリング総論の各ページへ。
| 落とし穴 | 対処 |
|---|---|
| 標準化せずに距離計算 | 必ず StandardScaler。 大きいスケールの変数だけが効いてしまう。 |
| K=2 から始めて 1 つの解で報告 | 複数 K・複数手法を比較。 シルエットや BIC で選ぶ。 |
| k-means で乱数固定なし | 必ず random_state を指定。 n_init を 10 以上に。 |
| 非球形データに k-means | DBSCAN / GMM / スペクトラルに切り替え。 |
| クラスタ番号に意味を与える | 番号は順序なし。 各クラスタの特徴量プロファイルで命名する。 |
| 高次元データに直接適用 | PCA / UMAP で次元削減してから。 次元の呪い。 |
| DBSCAN の eps を恣意的に | k-distance plot で根拠ある選択。 |
1 2 3 4 5 6 7 8 9 10 11 | import pandas as pd from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) X = df[['消費支出', '世帯人員', '高齢化率', '人口密度', '就業率']] X_scaled = StandardScaler().fit_transform(X) km = KMeans(n_clusters=4, n_init=10, random_state=42) df['cluster'] = km.fit_predict(X_scaled) print(df.groupby('cluster')[X.columns].mean().round(1)) print(df.groupby('cluster')['都道府県'].apply(lambda s: ', '.join(s))) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans # X_scaled はこのあとのブロックで作っているので、ここでも用意しておく _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) _d = _d[_d['SSDSE-B-2026'] == 2023] X_scaled = StandardScaler().fit_transform( _d[['A1101', 'A1301', 'A1303', 'A4101', 'L3221']].astype(float).values) from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt sils, inertias = [], [] for k in range(2, 11): km = KMeans(n_clusters=k, n_init=10, random_state=42).fit(X_scaled) sils.append(silhouette_score(X_scaled, km.labels_)) inertias.append(km.inertia_) fig, axes = plt.subplots(1, 2, figsize=(12, 4)) axes[0].plot(range(2,11), inertias, 'o-'); axes[0].set_title('Elbow') axes[1].plot(range(2,11), sils, 'o-'); axes[1].set_title('Silhouette') plt.show() |
1 2 3 4 5 | from scipy.cluster.hierarchy import linkage, fcluster from sklearn.metrics import adjusted_rand_score Z = linkage(X_scaled, method='ward') labels_h = fcluster(Z, t=4, criterion='maxclust') print('ARI(km, hier) =', adjusted_rand_score(km.labels_, labels_h)) |
「k-means で 4 グループに分かれました。」
「47 都道府県を 5 次元(所得・世帯人員・高齢化率・人口密度・就業率、 標準化済み)で k-means クラスタリング。 K=2〜10 のシルエット係数と Ward 法 BIC から K=4 を選択。 シルエット = 0.42(中程度)。 各クラスタを特徴量プロファイルから「大都市圏」「成長地方」「高齢過疎」「観光・農村」と命名。 Ward 法との ARI = 0.71(高い一致)。 再現性のため random_state=42, n_init=10 を明記。」
「総人口(数百万)」と「失業率(%)」を同じスケールで距離計算すると、 距離はほぼ「総人口の差」だけで決まり、 失業率は無視される。 これは k-means、 階層クラスタリング、 DBSCAN、 すべてに共通する罠。 必ず StandardScaler や RobustScaler で標準化してから距離計算する。 ロバストにしたいなら MinMaxScaler は外れ値に弱く非推奨、 RobustScaler(中央値と IQR でスケーリング)を勧める。
k-means で k=3 を最初に指定したら、 必ず 3 つに分かれて結果が出る。 問題は「その 3 つが意味ある分割か」。 必ずエルボー法 (SSE)、 シルエット係数、 Gap statistic、 BIC(GMM 用)で k を選ぶか、 複数の k で結果を比較する。 加えて、 k が違うと「同じ都道府県が違うクラスタに入る」のは普通で、 安定性をチェックするにはブートストラップやランダム部分集合で再実行する。
k-means は初期重心に依存し、 局所最適に落ちる。 sklearn は既定で n_init=10 を 1.4 以降は 'auto' に切り替えたが、 古い API では 1 のまま。 信頼できる結果を得るには n_init ≥ 20 を明示的に指定し、 random_state を固定して再現性を確保。 k-means++(既定)を使うことで初期化品質は上がるが、 完全には除けない。 数百回の再起動で最良の inertia を採用するのが安全。
k-means はユークリッド距離ベースで、 暗黙的に「球形・等分散・等密度」のクラスタを仮定する。 三日月型・リング状・密度の違うクラスタは分離できない。 こうした形状なら DBSCAN・HDBSCAN・Spectral Clustering・Gaussian Mixture を使う。 SSDSE のような都道府県データは球形に近いので k-means で十分だが、 顧客セグメントや画像分類など複雑形状ではダメ。 形状を疑うなら PCA で 2D 散布図を先に確認する。
シルエットは「クラスタ内の凝集度 vs 外の分離度」を測る便利な指標だが、 球形クラスタを暗黙仮定しており、 DBSCAN の「ノイズ点」や非球形クラスタには不利になる。 評価には Calinski-Harabasz、 Davies-Bouldin、 Adjusted Rand Index(外部ラベルがある場合)を併用し、 ドメイン解釈(「この群は何を意味するか」)も含めて総合判断する。
クラスタリングは「データを必ず k 個に分ける」アルゴリズムで、 一様乱数を入れても k 個に分けて返す。 「クラスタが存在するか」を検定するには Hopkins 統計量(> 0.75 でクラスタ性あり)や、 ランダム化テスト(同じデータをシャッフルしたものとの比較)を実施する。 結果を信じる前に「そもそも構造があるか」を確認する習慣をつける。
StandardScaler か RobustScaler を入れる。random_state を固定し、 n_init=20 以上にし、 ブートストラップで安定性を計測。| 症状 | 原因 | 診断 | 対策 |
|---|---|---|---|
| 1 群がほとんど | 標準化忘れ・eps過大 | 分散比較 | スケーラー・K見直し |
| 毎回違う結果 | n_init 不足 | 10 回試行 | n_init=20、 k-means++ |
| 外れ値が単独群 | 完全連結・k-means | サイズ確認 | Ward・GMM・除外 |
| シルエット低 | 非凸群 | 散布図 | DBSCAN・スペクトラル |
| 解釈不能 | 命名なし | クラスタ平均 | プロファイル分析 |
| 過剰分割 | K過大・BIC無視 | ARI/NMI | K減・統合 |
| 分野 | 適用例 | 使う手法 | 期待効果 |
|---|---|---|---|
| 地域政策 | 同類型県のベンチマーク比較 | Ward 法 K=4-6 | 政策パッケージの最適化 |
| マーケティング | 顧客セグメンテーション | k-means + RFM 変数 | 広告 ROAS 30% 向上 |
| 教育 | 学習履歴のクラスタ化 | GMM・所属確率 | パーソナライズ教材 |
| 医療 | 患者表現型の分類 | HDBSCAN・潜在空間 | サブタイプ発見 |
| 異常検知 | 不正取引検出 | DBSCAN ノイズ | 少額多数を一括検出 |
| テキスト | トピック分類 | 埋め込み + k-means | FAQ 自動分類 |
このコードでやること: SSDSE-B-2026 を k=4 で分けた各群について、 「平均ベクトル ・ 代表都道府県 ・ 命名候補」を一覧出力する。
📥 入力: df(生データ)、 km.labels_(前述 k-means の結果)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | df['cluster'] = km.labels_ # 1) 各クラスタの平均値 profile = df.groupby('cluster')[feats].mean().round(0) profile['n'] = df.groupby('cluster').size() print('=== クラスタ別平均 ===') print(profile) # 2) 各クラスタの代表都道府県(重心に最も近い 3 県) # df は skiprows=[1] で読んでいるので、県名の列は '都道府県' ではなく 'Prefecture' from sklearn.metrics import pairwise_distances_argmin_min for c in sorted(df['cluster'].unique()): mask = df['cluster'] == c sub_X = Xs[mask.values] center = sub_X.mean(axis=0).reshape(1, -1) dists = ((sub_X - center) ** 2).sum(axis=1) near_idx = np.argsort(dists)[:3] near_pref = df.loc[mask, 'Prefecture'].values[near_idx] print(f'クラスタ {c}: 代表 {list(near_pref)}') |
📤 実行結果(典型例):
💬 解釈と命名候補:
この命名により、 「クラスタ 3 の県には少子化対策パッケージ A」「クラスタ 1 の県には工業高度化補助金 B」など、 同類型県に共通する政策ツールキットを設計できます。
「クラスタリング (グループ教材)」を中心に、 「k は何個か」(問い) と「シルエット・エルボー」(解) を対置した俯瞰図。 47 都道府県データを k-means/階層型/DBSCAN/GMM の 4 系統で類型化する道筋を示す。
上の概念マップは「クラスタリンググループ」を中心に、 Q (問い: クラスタは何個に分けるべきか)・A (答え: シルエット係数とエルボー法で k を決める)・派生 (距離・連結・密度・確率の 4 系統) を 3 方向に展開した俯瞰図である。 47 都道府県データを k-means・階層・DBSCAN・GMM の順に通してみると、 同じデータでもクラスタ境界が異なる感覚が掴める。
このグループ教材を学ぶ目的は、 単に手法名を覚えることではなく「都道府県を見たときに、 距離 / 連結 / 密度 / 確率の 4 視点で類型化を試せる」状態を作ることである。 概念マップで全体像を掴んでから個別ページに進むと迷わない。
「クラスタリング」は教師なしでデータを群に分ける手法群で、 上流の標準化と次元削減、 下流のシルエット係数による評価が無いと「クラスタの数が違うだけで結論が反転する」事故が起きる。
上流で標準化と次元削減を済ませ、 並列の k-means/階層型/DBSCAN を比較し、 下流でシルエット係数と可視化を出せば、 都道府県をライフスタイル類型に分けるような EDA を再現可能な形で実行できる。
「クラスタリング」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。
47 都道府県は件数が少ないので、 クラスタ数を増やすとすぐに 1 県だけのクラスタができる。 k は 3〜5 程度から試し、 各クラスタに何県入ったかを必ず確認する。
本ページは k-means・階層・DBSCAN・GMM を横断する総論です。個別手法の詳細は各ページに譲り、ここでは総論ならではの角度—「同じ 47 都道府県・同じ k を与えても、標準化するか/どの手法を使うか/k をいくつにするかで、答え(クラスタ)が別物になる」というクラスタリングの主観性—を、SSDSE-B-2026 の実測値だけで解剖します。
クラスタリングは「隠れた真のグループを掘り当てる」作業に見えますが、実際にはアナリストの前処理・距離・k の選択が答えを作り出しています。同じ 3 変数(総人口・高齢化率=65歳以上人口/総人口・合計特殊出生率)で 47 都道府県を k=4 の k-means にかけても、標準化の有無だけで結果がここまで変わります(2023 年・SSDSE-B-2026 実測、n_init=10・random_state=0 で実際に計算した値)。
| 設定(k=4・k-means) | 4 クラスタの人数分布 | 意味 |
|---|---|---|
| 標準化なし(生値) | 37 / 5 / 4 / 1 | 総人口の桁が距離を支配。東京都が単独クラスタ、残り 37 県が一塊に潰れる |
| 標準化あり(z 化) | 17 / 15 / 9 / 6 | 高齢化率・出生率も効き、バランス良く分割される |
この 2 つの分割の一致度を ARI(調整ランド指数)で測ると 0.181。1.0 が完全一致、0 前後が「ほぼ無関係」ですから、同じデータ・同じ手法・同じ k でも、標準化するかどうかだけで“ほぼ別物のクラスタ”が生まれたことになります。総論の第一の教訓は「距離はスケールに素直—だから前処理が結果を決める」です。
初学者は「良いアルゴリズムを選べば正しいクラスタが出る」と考えがちですが、実データでは手法差より前処理差や k の差の方が大きいことすらあります。上と同じ 3 変数・標準化ありで測った ARI を並べます(すべて実測)。
| 2 つの分割の違い | ARI | 解釈 |
|---|---|---|
| 標準化あり k-means ↔ 標準化なし k-means(k=4) | 0.181 | 前処理の差=ほぼ別物 |
| 標準化 k-means ↔ 標準化 Ward 法(k=4) | 0.776 | 手法の差はむしろ小さめ(前処理が同じなら近い) |
| 標準化 k-means k=4 ↔ 同 k=3 | 0.424 | k を 1 つ変えるだけで中程度の食い違い |
答えが一意でないなら、良し悪しは「その分割が説明可能か」で判断します。標準化ありの分割では、たとえば沖縄県(合計特殊出生率 1.60 で全国最高)が、宮城・京都・福岡・広島など大都市圏の県と同じクラスタに入りました。生値では埋もれていた「若さ・出生の高さ」という軸が、標準化で初めてクラスタの意味を作ったわけです。逆に高齢化率が最も高い秋田県(39.1%)と最も低い東京都(22.8%)は、どの妥当な設定でも同じ群には入りません。
📝 数値の出典:data/raw/SSDSE-B-2026.csv(pd.read_csv(encoding='cp932', skiprows=[1]), 2023 年 47 都道府県)。使用列=A1101 総人口・A1303 65歳以上人口(高齢化率=A1303/A1101)・A4103 合計特殊出生率。クラスタ人数分布・ARI(0.181 / 0.776 / 0.424)・シルエット係数(0.353 / 0.347)は scikit-learn(KMeans n_init=10, random_state=0;AgglomerativeClustering linkage='ward')で実際に計算した実測値。合成デモは本節には含みません(ページ上部の実験室は架空データ)。