このページを高速ナビゲートするための索引チップです。クリックで該当セクションへ。
「spectral clustering」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「spectral clustering」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「spectral clustering の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🍰 まずはやさしく
データの繋がりで分ける手法です。
複雑な形のグループを分けるために使います。
スマホの友達関係でグループを作るようなものです。
この手法の結論を短くまとめます。
🍰 まずはやさしく
データの繋がりを見る道具です。
普通のやり方で分けられないデータを分けるために使います。
部活のメンバーを自然なグループに分けるようなものです。
この手法がどのような場面で役立つかを紹介します。
クラスタリングのチュートリアルや論文で、 こんな表現を見たことがあるでしょう:
これらは スペクトラルクラスタリング(Spectral Clustering) の話です。 k-means が「球状クラスタ」を仮定するのに対し、 スペクトラル手法は 「データの繋がり方(グラフ)」 から自然なクラスタを抽出します。 半月、 渦巻き、 環など、 k-means が完全に失敗するデータでも見事にクラスタを切り分ける、 1990 年代後半〜2000 年代に発達した強力な手法群です。
このページは スペクトラルクラスタリング を解説する用語ページです。
カテゴリ:クラスタリング・教師なし学習
ジャストインタイム型データサイエンス教育の一環として、必要な時に参照し、関連概念とともに学べる構成になっています。
🍰 まずはやさしく
繋がりが弱い場所で切るイメージです。
自然なグループの境界線を見つけるために使います。
SNSで別のサークル同士の繋がりが少ない場所を探すようなものです。
なぜこの方法でグループが分けられるのかを説明します。
2 つの 半月型データ を考えましょう。 人間の目には明らかに 2 つのクラスタですが、 k-means は「中心からの距離」で割り当てるため、 中心が両クラスタの中間に来てしまい、 上下半分でばっさり切る 失敗をします。
スペクトラルクラスタリングは違うアプローチを取ります:
SNS の友達ネットワークを思い浮かべてください。 同じサークルの人同士は密に繋がり、 別サークルとは疎。 ネットワーク全体を 2 グループに分けるとき、 「切るエッジが最も少ない場所」 で分割すれば、 サークル境界を自然に発見できます。 これがスペクトラルクラスタリングのアイデア:
| ステップ | やること | 意味 |
|---|---|---|
| ① グラフ構築 | 類似度行列 $W$ を作る | 「点と点の繋がりの強さ」を定量化 |
| ② スペクトラル分解 | ラプラシアン $L$ の下位 k 固有ベクトル | クラスタ構造が「ばらける」低次元表現 |
| ③ k-means | 固有ベクトル空間で k-means クラスタリング | 新しい座標では球状になるので、 k-means が機能 |
スペクトラルクラスタリングの理論的進歩を最も大きく加速したのは、 1997-2000 年の Shi & Malik による Normalized Cut(NCut) の画像セグメンテーション応用です。 IEEE TPAMI 2000 の論文「Normalized Cuts and Image Segmentation」は被引用 25,000 回以上の歴史的論文。
1 枚の画像をグラフに変換する方法:
MinCut は「1 ピクセルだけ切り離す」極小カットを返す問題があった。 NCut は カットを「クラスタの体積」で正規化することで、 バランスの取れた分割を導きます:
2010 年代以降、 画像セグメンテーションは深層学習(U-Net, Mask R-CNN, SAM)に主役を譲りましたが、 スペクトラル法は今でも:
などで現役。 特に 「ラベル付きデータが少ない場合」に教師なし手法として有効です。
2017 年以降、 GNN(Graph Neural Network)が爆発的に発達。 これとスペクトラルクラスタリングは 同じグラフラプラシアン を共有する近縁手法です。
有名な Graph Convolutional Network(GCN) は、 グラフ畳み込みを正規化ラプラシアン上で定義:
| 項目 | スペクトラル CL | GCN |
|---|---|---|
| 共通の核 | 正規化ラプラシアン | 正規化ラプラシアン |
| 目的 | 教師なしクラスタリング | 半教師あり分類 |
| 処理 | 固有値分解(1 回) | 線形変換 + 活性化(多層) |
| 表現 | 固有ベクトル空間 | 学習された埋め込み |
| 監督 | 不要 | 一部ラベルを使う |
「スペクトラルクラスタリング」を GNN の文脈で見ると、 「1 層のラプラシアン演算 + 固有ベクトル抽出 + k-means」と理解できる。 一方 GCN は「複数層の非線形ラプラシアン演算 + 分類」。 学習可能性が異なるだけで、 数学的基盤は共通です。
スペクトラルクラスタリングの威力を体感するため、 sklearn 内蔵の代表的データセットで他手法と比較:
| データセット | k-means | 階層的 | DBSCAN | Spectral |
|---|---|---|---|---|
| blobs(球状クラスタ) | ◎ | ◎ | ◎ | ◎ |
| moons(半月) | ✗ | △(ward) | ◎ | ◎ |
| circles(同心円) | ✗ | △ | ◎ | ◎ |
| varied(密度違い) | △ | ○ | △ | ○ |
| aniso(楕円) | △ | ○ | △ | ○ |
| SSDSE 47 県 | ○ | ○ | △(小規模で密度低) | ○ |
凡例:◎ 完璧/○ ほぼ正確/△ 部分的に正確/✗ 失敗
結論:sklearn 公式の「Comparing different clustering algorithms on toy datasets」のページが包括的で参考になります。 一般則:
スペクトラルクラスタリングは パラメータ感度が高い 手法です。 「とりあえずデフォルト」では失敗することが多いため、 体系的なチューニング手順が重要。
| パラメータ | 推奨値 | 影響 |
|---|---|---|
| n_clusters (k) | 固有値ギャップで自動推定 or 3-10 で総当たり | クラスタ数。 最重要。 シルエットで検証 |
| affinity | 'nearest_neighbors' または 'rbf' | 類似度関数。 k-NN は局所性重視、 RBF は連続的 |
| n_neighbors (k-NN) | $\log_2 n$ または 5-30 | k 大 → 過度に繋がる、 k 小 → 断片化 |
| gamma (RBF) | $1/d$(次元数の逆数)または median heuristic | σ = 1/√(2γ)。 大きすぎ → 全て類似、 小さすぎ → 全て無関係 |
| assign_labels | 'kmeans' または 'discretize' | 固有ベクトル空間での割当方法 |
RBF カーネルの幅 $\sigma$ を、 全点対距離の中央値に設定する経験則:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler # X はこのあとのブロックで作っているので、ここでも用意しておく # (2023 年の 47 都道府県 × 数値列を標準化した行列) _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) _d = _d[_d['SSDSE-B-2026'] == 2023].reset_index(drop=True) X = StandardScaler().fit_transform( _d[['A1101', 'A1301', 'A1303', 'A4101', 'A4200', 'L3221']].astype(float)) from scipy.spatial.distance import pdist sigma = np.median(pdist(X)) gamma = 1.0 / (2 * sigma**2) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from sklearn.cluster import SpectralClustering from sklearn.metrics import silhouette_score import itertools best = {'score': -1, 'params': None} for k, n_nbrs in itertools.product([3,4,5,6,7], [5,10,15,20]): sc = SpectralClustering(n_clusters=k, affinity='nearest_neighbors', n_neighbors=n_nbrs, random_state=0) try: labels = sc.fit_predict(X) if len(set(labels)) < 2: continue score = silhouette_score(X, labels) print(f'k={k}, n_nbrs={n_nbrs}: silhouette={score:.3f}') if score > best['score']: best = {'score': score, 'params': (k, n_nbrs)} except Exception as e: pass print(f'\\n最適: k={best["params"][0]}, n_nbrs={best["params"][1]}, score={best["score"]:.3f}') |
スペクトラルクラスタリングで得た「47 都道府県の 4 クラスタ」は、 様々な実務応用に活かせます。
同じクラスタの県は社会経済プロファイルが類似しているため、 1 県で実施した政策の効果を 同クラスタの他県に外挿できる可能性が高い。 例:「秋田で実施した高齢者支援策の効果」は「島根、 高知、 山形」でも類似結果が期待できる。 これは 合成統制法(synthetic control method)の前段として使われます。
小売チェーンの出店戦略で、 既出店の県と「同クラスタの未出店県」を優先候補に。 顧客プロファイルが類似していると期待でき、 既存ノウハウの転用が利く。
地方自治体の比較分析で、 「自県と類似する県」をベンチマークとして選ぶことで、 客観的な比較が可能になる。 「東京と秋田を比較」は不公平だが、 「秋田と高知、 島根、 山形」なら同じクラスタ内での比較で意味がある。
ある県で特定指標が欠損していたら、 同クラスタの他県の平均値で補完するのが、 全国平均で補完するより精度が高い。 これは k-NN imputationの応用版。
「過去 5 年間、 クラスタ ④(過疎県)に属していた A 県が、 今年クラスタ ③(地方経済型)に移動した」というような変化は、 構造的転換のシグナル。 産業構造や人口動態の重要な変化を捉える指標になります。
スペクトラル法で得た 4 クラスタが本当に妥当かを、 3 つの観点から検証する手順を示します。
各クラスタの主要指標の平均値・中央値を比較し、 解釈可能なラベルが付けられるか確認:
| クラスタ | 平均人口(万人) | 平均所得(万円) | 高齢化率(%) | 解釈ラベル |
|---|---|---|---|---|
| ① 大都市圏 | 650 | 425 | 26 | 大規模・高所得・高進学率 |
| ② 地方中核 | 200 | 325 | 30 | 中規模・産業基盤あり |
| ③ 地方経済型 | 180 | 305 | 28 | 製造業中心・持家率高 |
| ④ 過疎・高齢化 | 100 | 250 | 35 | 人口減少・高齢化進行 |
主要指標について、 各クラスタペア間で t 検定(または非パラメトリックな Mann-Whitney U 検定)を実施:
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 | import numpy as np import pandas as pd import itertools from scipy.stats import ttest_ind from sklearn.preprocessing import StandardScaler from sklearn.cluster import SpectralClustering 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).set_index('Prefecture') features = df.select_dtypes(include=[np.number]).dropna(axis=1) features['高齢化率'] = features['A1303'] / features['A1101'] * 100 # クラスタ割当(result)もここで作る _X = StandardScaler().fit_transform(features[['A1101', 'A1303', 'A4101', 'L3221']]) _lab = SpectralClustering(n_clusters=4, affinity='nearest_neighbors', n_neighbors=10, random_state=0).fit_predict(_X) result = pd.DataFrame({'都道府県': features.index, 'cluster': _lab}) # 平均所得・大学進学率は SSDSE-B-2026 に無いので、実在する指標で比べる key_metrics = ['A1101', 'A4101', 'L3221', '高齢化率'] for metric in key_metrics: print(f'\n=== {metric} ===') for c1, c2 in itertools.combinations(range(4), 2): x1 = features.loc[result[result.cluster == c1].都道府県, metric] x2 = features.loc[result[result.cluster == c2].都道府県, metric] if len(x1) < 2 or len(x2) < 2: continue t, p = ttest_ind(x1, x2) sig = '***' if p < 0.001 else '**' if p < 0.01 else '*' if p < 0.05 else '' print(f' クラスタ {c1} vs {c2}: t={t:.2f}, p={p:.4f} {sig}') |
データを 100 回リサンプリングし、 毎回スペクトラルクラスタリングを実施。 得られた割当の Jaccard 類似度が高ければクラスタが安定:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from sklearn.cluster import SpectralClustering # labels_sc はこのブロックの比較対象。ここで求めておく labels_sc = SpectralClustering(n_clusters=4, affinity='nearest_neighbors', n_neighbors=10, random_state=0).fit_predict(X) from sklearn.utils import resample from sklearn.metrics import adjusted_rand_score n_boot = 100 aris = [] for seed in range(n_boot): idx = resample(np.arange(len(X)), random_state=seed) X_boot = X[idx] sc_boot = SpectralClustering(n_clusters=4, affinity='nearest_neighbors', n_neighbors=10, random_state=0) labels_boot = sc_boot.fit_predict(X_boot) # 元データの該当点と一致度を計算 aris.append(adjusted_rand_score(labels_sc[idx], labels_boot)) print(f'平均 ARI(bootstrap): {np.mean(aris):.3f}') print(f'95% CI: [{np.quantile(aris, 0.025):.3f}, {np.quantile(aris, 0.975):.3f}]') # ARI > 0.7 ならクラスタは安定 |
Tibshirani ら(2001)の Gap 統計量で、 「ランダムデータと比較した実データのクラスタ構造の強さ」を測定:
これら全てが満たされて初めて「妥当なクラスタリング」と言えます。 1 つでも怪しい場合、 パラメータ再調整やデータ前処理を見直す。
データ点同士の類似度をグラフのエッジ重みで表現し、そのグラフを「切る」コスト最小の分割を、ラプラシアン行列の固有ベクトル空間で k-means によって実現する。非凸な塊(三日月形・同心円)も分離できる。
| 場面 | 使い方 |
|---|---|
| 探索的データ分析 | 分布や関係性の最初の確認 |
| モデル比較 | 仮定の妥当性を裏付ける指標として |
| レポート作成 | 標準的な要約統計量・指標として明記 |
スペクトラルクラスタリングは 「グラフラプラシアンの固有ベクトル空間で k-means する」。 以下 3 つの図で「グラフ構築」「ラプラシアン固有分解」「非凸クラスタへの強み」を視覚化する。
→ 類似度がエッジ重みに。 RBF カーネルや k-NN グラフが代表的。 σ の選び方で結果が大きく変わる。
→ 「固有値ジャンプ (eigengap)」直前の数がクラスタ数 k。 連結成分が完全に分離していればその数だけ λ=0 が並ぶ。
→ k-means は 「球状クラスタ前提」のため非凸 (リング・三日月) で失敗。 スペクトラルは 「グラフ上の連結性」でクラスタを定義するので非凸でも分離可能。
スペクトラルクラスタリングの性能は、 入力の類似度行列 W の作り方でほぼ決まります。 代表的な選択肢は (1) ガウシアンカーネル W_ij = exp(-||x_i - x_j||² / (2σ²))、 (2) k 近傍グラフ(各点について上位 k 個の隣だけ繋ぐ)、 (3) ε-ball グラフ(距離が閾値 ε 以下の点だけ繋ぐ)、 (4) コサイン類似度ベース、 の 4 つです。 SSDSE-B-2026 の 47 都道府県を「総人口 (A1101)・65歳以上人口 (A1303)・出生数 (A4101)・消費支出 (L3221)」の 4 次元でクラスタリングする場合、 標準化後にガウシアンカーネルを使い、 σ をデータの中央距離(median heuristic)に取ると安定します。 k 近傍グラフは「東京都のような外れ値が他の点とまばらに繋がる」のを防ぐので、 異質な大都市を含むデータでは有効です。
グラフラプラシアンには L = D - W(非正規化)、 L_sym = I - D^(-1/2) W D^(-1/2)(対称正規化、 Ng-Jordan-Weiss 2002)、 L_rw = I - D^(-1) W(ランダムウォーク正規化、 Shi-Malik 2000)の 3 種があります。 クラスタサイズに偏りがあるデータでは正規化ラプラシアンの方が「小さなクラスタが大きなクラスタに吸収される」現象を防げるので、 ほとんどの実装が L_sym を既定としています。 scikit-learn の SpectralClustering(affinity='rbf') も内部的に L_sym を計算しています。 47 都道府県の場合、 北海道のように面積が突出する点があるため、 正規化版を選んでおくのが安全です。
スペクトラル法の利点の一つは、 ラプラシアンの固有値を昇順に並べたとき「k 番目と k+1 番目の間で急に値が跳ぶ」場所を見ることで適切なクラスタ数 k を推定できることです。 これを eigengap heuristic と呼びます。 SSDSE-B-2026 を 4 次元で類似度行列に変換すると、 固有値は概ね [0.00, 0.02, 0.05, 0.08, 0.41, 0.55, ...] のように分布し、 4 番目と 5 番目の間に大きなギャップが現れます。 この場合 k=4 を採用するのが定石です。 ただし eigengap が明瞭でないデータも多いので、 シルエットスコアや Calinski-Harabasz 指標で補完するのが実務的です。
スペクトラル法は最後にスペクトル空間(固有ベクトルからなる低次元埋め込み)で k-means を実行する 2 段階方式です。 この最終 k-means の初期化が乱数に依存するため、 同じ入力でも実行ごとにクラスタ割当が変わることがあります。 対策は (1) n_init=20 など複数初期化を試して最良を採用、 (2) init='k-means++' を必ず使う、 (3) assign_labels='discretize' を選んで離散最適化に切り替える、 のいずれかです。 scikit-learn では SpectralClustering(n_clusters=4, assign_labels='kmeans', random_state=42, n_init=20) のように指定すると再現性を確保できます。
スペクトラル法は固有値分解が O(n³)、 メモリも O(n²) なので、 サンプル数 n が 1 万を超えると現実的には実行不可能になります。 47 都道府県のような小規模では問題ありませんが、 全国 1718 市区町村に拡張する場合は (a) Nyström 近似で固有ベクトルを部分計算する、 (b) k 近傍グラフで W を疎行列化する、 (c) Multilevel スペクトラル法で粗視化と精密化を交互に行う、 のいずれかが必要です。 Python なら sklearn.cluster.SpectralClustering(eigen_solver='amg', n_components=k) と AMG (Algebraic Multigrid) を選ぶことで疎行列向けの高速化が効きます。
n_init を大きく、 assign_labels='discretize')6 問中 5 問以上正解で「スペクトラルクラスタリングの実装と限界を理解した」と自己評価できる。 5 問未満ならアルゴリズムと固有値ギャップの節を再読する。
k-means が「重心からの距離」に基づくのに対し、 スペクトラル法は「グラフ上の連結性」に基づきます。 47 都道府県を「人口・面積・高齢化率」の 3 次元で分けるとき、 k-means は「人口が大きい東京・神奈川・大阪を 1 つにまとめる」ように動きますが、 スペクトラル法は「人口は遠くても面積が似ている」点を同じクラスタにまとめます。 つまりスペクトラル法は「ユークリッド距離では遠いが類似度の鎖でつながる」点を同じクラスタにできるため、 三日月形・らせん形などの非凸クラスタを発見できます。 一方で計算量が大きく、 解釈の難しさ(重心がない)もデメリットです。
階層クラスタリングは樹形図 (dendrogram) を出力するので「どこで切るか」を後から決められる柔軟性があり、 SSDSE-B-2026 のような小規模データでは可視化と相性が良いです。 一方スペクトラル法は事前にクラスタ数 k を決める必要があるものの、 非凸クラスタの発見性能では階層法を上回ります。 実務では両方を並行して動かし、 「階層法の樹形図で全体構造を眺め、 スペクトラル法で k=4 を採用」のようにハイブリッド運用するのが定石です。
DBSCAN は密度ベースでクラスタ数を自動決定でき、 ノイズ点も検出できる強力な手法です。 しかしクラスタ間で密度に差があると検出が不安定になり、 高次元では距離が均質化して破綻しやすい欠点があります。 スペクトラル法は密度差に強く、 高次元でも事前の特徴選択や PCA と組み合わせれば動きますが、 ノイズ検出機能は持ちません。 SSDSE-B-2026 の 4 次元なら DBSCAN とスペクトラル法のどちらも適用可能で、 結果を比較するのが教育的です。
スペクトラル法に限らずクラスタリングの良し悪しを定量化するには複数指標を併用します。 (1) シルエットスコア: 各点について「同じクラスタ内の平均距離」と「最近接他クラスタへの平均距離」を比較。 -1〜1 で 1 に近いほど良い。 (2) Calinski-Harabasz 指標: クラスタ間分散とクラスタ内分散の比。 大きいほど良い。 (3) Davies-Bouldin 指標: 隣接クラスタへの類似度の平均。 小さいほど良い。 (4) 外部評価 (Adjusted Rand Index): 真のラベルがある場合に一致度を測定。 SSDSE-B-2026 では真のラベルがないので (1)〜(3) を使い、 さらに地理的・経済的な意味解釈を組み合わせて妥当性を判定します。
スペクトラルクラスタリングの中核は「グラフラプラシアン行列 L = D - W (D は次数行列、 W は重み行列) の固有値分解」です。 固有値ゼロの多重度がグラフの連結成分数、 小さい固有値に対応する固有ベクトルがクラスタ構造を示します。 SSDSE-B-2026 で 47 都道府県の「人口移動量」を W に、 都道府県間の Laplacian を構築すれば、 地域的なクラスタが固有ベクトルで自然に現れます。 数学的には L は半正定値行列であり、 固有値はすべて非負、 ゼロ固有値の数が連結成分数と一致する性質 (Fiedler の定理) が基礎となります。 第二最小固有値 (Fiedler 値) はグラフの「代数的連結度」を表し、 これが小さいほどグラフは分割しやすいことを意味します。 47 都道府県を「東日本」「西日本」のように 2 分割するなら Fiedler ベクトルの符号で分けるのが古典的アプローチで、 これを多クラスへ拡張したのが現代的スペクトラルクラスタリングです。
スペクトラルクラスタリングには大きく 2 系統あります。 (1) Ratio Cut: 各クラスタのサイズで正規化し、 「カット重み / クラスタサイズ」を最小化。 (2) Normalized Cut (Shi-Malik 2000): クラスタ内エッジ重み総和で正規化し、 「カット重み / クラスタ内重み総和」を最小化。 後者は大規模グラフでバランスの良いクラスタを得られるため標準的に用いられます。 Normalized Cut の最小化問題は緩和すると「対称正規化ラプラシアン L_sym = I - D^(-1/2) W D^(-1/2)」の固有値問題と等価になり、 Ng-Jordan-Weiss (2002) アルゴリズムが定番です。 SSDSE で 47 都道府県を「経済規模で偏らないように分割したい」場合は Normalized Cut、 「単純にサイズが揃ったクラスタが欲しい」場合は Ratio Cut が適しています。 sklearn の SpectralClustering はデフォルトで Ng-Jordan-Weiss 方式 (Normalized Cut 系) を採用しています。
スペクトラルクラスタリングの結果はアフィニティ行列 W の構築方法に大きく依存します。 主な方式: (1) k-NN グラフ: 各点の k 近傍を接続、 局所構造を保ちやすい、 (2) ε-ball グラフ: 半径 ε 以内を接続、 密度が均一なデータで有効、 (3) Gaussian カーネル: W_ij = exp(-||x_i - x_j||^2 / (2σ^2))、 滑らかな類似度、 (4) 全結合: すべてを Gaussian で重み付け、 計算量大。 σ の選択が結果を大きく左右します。 経験則として σ = 中央値 (median heuristic) や σ = k 番目最近接距離 (Zelnik-Manor & Perona 2004 の self-tuning) が良く使われます。 SSDSE-B-2026 で「都道府県間の経済的距離」を構築する際は、 ユークリッド距離か Mahalanobis 距離を選び、 さらに分散を考慮した self-tuning σ を採用すると安定します。 k-NN グラフは k=8 が経験的に頑健で、 k が大きすぎると全結合に近づき、 小さすぎるとクラスタが分断します。
スペクトラル法は k-means と同じく事前にクラスタ数 k を決める必要があります。 主な選択法: (1) Eigengap heuristic: 固有値の差 |λ_(k+1) - λ_k| が大きい所で打ち切る、 理論的に最も自然、 (2) シルエットスコアの最大化、 (3) Davies-Bouldin Index 最小化、 (4) Calinski-Harabasz Index 最大化、 (5) BIC, AIC で情報量基準、 (6) Gap Statistic (Tibshirani et al. 2001) で帰無分布と比較。 SSDSE で 47 都道府県をクラスタリングする際、 k=4-8 が経験的に妥当 (関東・関西・東北・九州など地域ブロックの数に対応)。 Eigengap で 5 番目と 6 番目の固有値間に明確なギャップがあれば k=5、 なければシルエットスコアで決定します。 複数指標を組み合わせ、 さらにドメイン知識 (都道府県なら 8 地方区分) と整合させる二段階アプローチが実務的に推奨されます。
固有値分解の計算量は素朴に O(n^3) と高コストで、 n=10,000 を超えると標準実装では困難になります。 大規模化手法: (1) Nyström 法: 一部の点をランドマークとしてサンプリングし、 全体を低ランク近似、 (2) Lanczos 法: 反復で必要な部分固有ベクトルだけを計算、 疎行列に有効、 (3) Landmark MDS: 距離行列を低次元埋め込み、 (4) Random Projection: Johnson-Lindenstrauss 補題で次元削減、 (5) Power iteration clustering (Lin & Cohen 2010): 固有ベクトル計算を避けた近似。 SSDSE 47 県規模では問題ありませんが、 数万ノードの社会ネットワークでは必須です。 scipy では scipy.sparse.linalg.eigsh が Lanczos 系で実装されており、 k 個の最小固有値だけを高速に取得できます。
2017 年に提案された SpectralNet (Shaham et al.) はニューラルネットワークでスペクトラルクラスタリングを学習する革新的手法です。 特徴: (1) 制約付き目的関数で固有ベクトルを直接学習し、 (2) 大規模データに対応、 (3) Out-of-sample 拡張 (新規データへの適用) が可能、 (4) Siamese network で類似度自体も学習可能。 後続研究として DCN (Deep Cluster Network), JULE (Joint Unsupervised Learning), SCAN (Semantic Clustering by Adopting Nearest neighbors) などで深層学習統合が進んでいます。 SSDSE のような小規模データでは古典スペクトラル法で十分ですが、 画像・テキストなど高次元データには SpectralNet 系が必須となります。 PyTorch 実装も公開されており、 教育的には「古典 → SpectralNet」の流れを追体験できます。
GNN とスペクトラルクラスタリングは数学的に密接に関連します。 (1) GCN (Kipf-Welling 2016) の畳み込み演算は「正規化グラフラプラシアン × 特徴量」とほぼ等価で、 スペクトラル領域での畳み込みを近似、 (2) ChebNet (Defferrard et al. 2016) はチェビシェフ多項式で多次の伝播を効率化、 (3) GAT (Graph Attention Network) は attention 機構でエッジ重みを学習、 (4) Spectral GNN の研究が活発に進展中。 スペクトラルクラスタリングは GNN の理論的基礎を提供し、 「グラフ信号処理」という新分野の出発点となりました。 SSDSE-B-2026 を「47 都道府県のグラフ」として GCN で半教師あり分類するのは教育的応用として優秀で、 古典法と GNN の橋渡しになります。
優れた手法ですが限界もあります。 (1) 距離計算の計算量 O(n^2): アフィニティ行列構築自体が二乗オーダー、 (2) σ パラメータに敏感: 自動調整法が必須、 (3) k-means の初期化に依存: 最終段階の k-means が局所最適に陥る、 (4) 非凸クラスタは扱えるが重なるクラスタは苦手: ソフトクラスタリングは GMM 系の方が得意、 (5) 解釈性が k-means より低い: 重心がないので「典型例」を示しにくい、 (6) 新規データへの拡張困難: 全データ再計算が必要 (Out-of-sample 問題)。 これらを補うため (1) DBSCAN, OPTICS で密度ベース併用、 (2) HDBSCAN で階層密度クラスタリング、 (3) GMM でソフト所属確率、 (4) SpectralNet で Out-of-sample 対応、 など他手法との併用が現実的です。
47 都道府県を 10 指標 (総人口 A1101、 15歳未満人口 A1301、 65歳以上人口 A1303、 出生数 A4101、 合計特殊出生率 A4103、 婚姻件数 A9101、 標準価格・住宅地 C5401、 高等学校卒業者のうち進学者数 E3702、 消費支出 L3221、 年平均気温 B4101) で特徴量化し、 StandardScaler で標準化後にスペクトラルクラスタリング (k=5, Gaussian カーネル) を適用すると、 経験的に「大都市圏 (東京・大阪・愛知)」「地方中核 (福岡・宮城・広島・京都)」「農業地域 (東北・北信越)」「過疎地域 (中国・四国の一部)」「特殊 (沖縄、 北海道)」のような自然なクラスタが現れます。 既知の地域ブロック (関東、 東北 など 8 地方) と Adjusted Rand Index で比較すると ARI ≒ 0.4-0.6 程度になり、 「地理的近接性 + 経済的類似性 = ある程度一致」という解釈ができます。 一方 k=8 にすると地方区分との一致度が上がり、 k=3 だと「都市 / 中間 / 地方」のような粗いクラスタになります。
Shi-Malik (2000) は元々画像セグメンテーション用に Normalized Cut を提案しました。 アイデア: ピクセルをグラフのノードと見なし、 隣接ピクセルの色類似度・空間距離を組み合わせたエッジ重み W_ij = exp(-||F_i - F_j||^2 / σ_F^2) · exp(-||X_i - X_j||^2 / σ_X^2) でアフィニティを定義し、 Normalized Cut を最小化することで前景・背景を自動分離します。 現代では深層学習 (U-Net, Mask R-CNN, SAM) が主流ですが、 (1) 学習データ不要、 (2) 説明可能性が高い、 (3) 計算が決定論的、 という特性で再評価されています。 また、 SAM などの基盤モデルの後処理に Normalized Cut を組み合わせるハイブリッド手法も研究されています。
Facebook, Twitter, LinkedIn 等のソーシャルネットワークにおける「コミュニティ検出」「Modularity 最大化 (Newman 2004)」もスペクトラルクラスタリングの代表的応用です。 関連手法: (1) Louvain 法: 局所的 Modularity 最大化を貪欲に繰り返す高速アルゴリズム、 (2) Infomap: ランダムウォーク情報圧縮、 (3) Leiden アルゴリズム: Louvain の改良で連結性保証。 これらはすべてスペクトラル法と理論的に同根です。 SSDSE は社会ネットワークではありませんが、 「都道府県間人口移動量」を Edge にした都道府県ネットワーク解析は実施可能で、 関東圏・関西圏・中京圏のような「人の流れで結ばれた地域コミュニティ」を抽出できます。 これは経済地理学・人口社会学的にも意味のある分析です。
Python での実装選択肢: (1) sklearn.cluster.SpectralClustering が標準で最も簡便、 affinity を 'nearest_neighbors' / 'rbf' / 'precomputed' から選択可能、 (2) networkx + scipy.sparse.linalg.eigsh で疎行列対応のカスタム実装、 大規模に強い、 (3) graph-tool で C++ ベース高速大規模対応、 (4) kmodes ライブラリで混合データ (数値 + カテゴリ) 対応、 (5) scikit-network でグラフ専用機能群、 modularity ベースも可能。 SSDSE 実装例: from sklearn.cluster import SpectralClustering; sc = SpectralClustering(n_clusters=5, affinity='nearest_neighbors', n_neighbors=8, random_state=42); labels = sc.fit_predict(X) のように 3 行で実行できます。 再現性確保のため random_state を必ず固定し、 結果のクラスタラベルが入れ替わる「ラベル置換問題」に注意します。 評価には silhouette_score, adjusted_rand_score を併用します。
1999-2002 年に確立されたスペクトラルクラスタリングは、 深層学習隆盛の現代でも (1) 数学的厳密性: 線形代数の理論で完全に保証される、 (2) 説明可能性: 固有値・固有ベクトルで「なぜそのクラスタか」が示せる、 (3) グラフ構造を直接扱う: 非ユークリッドデータに自然対応、 という特徴で依然として重要です。 SSDSE-B-2026 のような小規模実データで適用すると、 線形代数 (固有値分解)、 グラフ理論 (ラプラシアン)、 機械学習 (クラスタリング) を一本の流れで体験できる優れた教材になります。 学習者は (1) 概念理解 (なぜ固有ベクトルでクラスタが分かるのか)、 (2) 実装演習 (sklearn で 3 行)、 (3) 評価実験 (シルエット・ARI)、 (4) ドメイン解釈 (47 都道府県の地理的意味)、 を通じて深い数学的・実用的スキルを獲得できます。 さらに発展として GNN, SpectralNet, グラフ信号処理へと橋渡しでき、 「古典と現代を貫く理論」としての価値は今後も失われません。
🍰 まずはやさしく
計算でグループ分けを行う仕組みです。
数学的に正しく分けるために使います。
買い物などのデータを数字の表にして処理するようなものです。
具体的な計算の手順と数式について解説します。
$n$ 個のデータ点 $\{x_1, \dots, x_n\}$ から $n \times n$ の類似度行列 $W$ を作ります。 代表的な 3 つの構成法:
次数行列 $D$ を $D_{ii} = \sum_j W_{ij}$(i 番目の点の総繋がり強度)とおいて、 3 種類のラプラシアンを定義:
ラプラシアン $L$ の 下位 k 個(最小固有値に対応する) の固有ベクトル $u_1, u_2, \dots, u_k$ を列に並べて $U \in \mathbb{R}^{n \times k}$ を作ります。 $U$ の各行 $y_i \in \mathbb{R}^k$ が点 $x_i$ の 新しい埋め込み座標。 これに対して通常の k-means を実行してクラスタを得る。
| ラプラシアン | 定義 | 正規化 | 推奨場面 |
|---|---|---|---|
| Unnormalized | $L = D - W$ | なし | 次数が均一 |
| Symmetric | $L_{\text{sym}} = D^{-1/2} L D^{-1/2}$ | 対称 | 一般用途 |
| Random Walk | $L_{\text{rw}} = D^{-1} L$ | 非対称 | 理論的に最も自然 |
Shi & Malik (2000) の normalized cut は $L_{\text{rw}}$、 Ng-Jordan-Weiss (2001) は $L_{\text{sym}}$ を使います。 von Luxburg (2007) のレビューで両者の関係が整理されました。 SSDSE-B-2026 の都道府県類似度行列のように次数が場所により異なる場合、 正規化版が安定します。
スペクトラルクラスタリング の中心となる数式・定義は次の通りです。
$$ L = D - W, \quad L_{\text{sym}} = I - D^{-1/2} W D^{-1/2} $$
スペクトラルクラスタリングは、 「データは高次元空間に埋め込まれた低次元マニフォールド(多様体)上に乗っている」というマニフォールド仮説と密接に関係します。
スペクトラルクラスタリングの「k-means する直前の固有ベクトル空間」をそのまま 次元削減の結果として使うのが Laplacian Eigenmaps。 数学的にはほとんど同じ枠組み:
| 手法 | 提案年 | 核心アイデア |
|---|---|---|
| PCA | 1901 (Pearson) | 共分散行列の固有ベクトル(線形) |
| Kernel PCA | 1998 (Schölkopf) | カーネル空間での PCA(非線形) |
| Isomap | 2000 (Tenenbaum) | 測地距離 + MDS |
| LLE | 2000 (Roweis & Saul) | 局所線形再構成の保存 |
| Laplacian Eigenmaps | 2003 (Belkin) | グラフラプラシアンの固有ベクトル |
| Diffusion Maps | 2006 (Coifman) | マルコフ拡散の固有値 |
| t-SNE | 2008 (van der Maaten) | 分布類似度の KL 最小化 |
| UMAP | 2018 (McInnes) | 位相幾何学的構造保存 |
スペクトラルクラスタリングと Laplacian Eigenmaps は 「同じ計算の異なる出口」 です。 「固有ベクトル空間で k-means したらクラスタ」、 「固有ベクトル空間の座標をそのまま返したら次元削減」。 これが分野横断的な強力さの源泉。
正規化ラプラシアン $L_{\text{sym}}$ の固有ベクトル分解は、 正規化された類似度行列のカーネル PCA と数学的に等価であることが示されています(Bengio ら, 2003)。 つまり:
スペクトラルクラスタリングは「グラフのクラスタリング」が本質なので、 もともとグラフデータ(SNS、 共起ネットワーク、 引用ネットワーク)に強力です。
| 手法 | 原理 | 強み・弱み |
|---|---|---|
| スペクトラル | ラプラシアン固有値分解 | 理論明快/大規模に弱い |
| Louvain | Modularity 最適化 | 高速/階層的構造/コミュニティ数自動 |
| Leiden | Louvain の改良版 | 2019 年提案、 現代の標準 |
| Label Propagation | ラベル伝播 | 超高速/結果が不安定 |
| Stochastic Block Model | 確率モデル | 理論的に堅牢/計算量大 |
| Infomap | 情報圧縮 | 有向グラフに強い |
Zachary's Karate Club(1977)は社会ネットワーク分析の古典データセット。 34 名の空手クラブが内部対立で 2 つに分裂した実際のデータ。 スペクトラル法は 分裂後の 2 派閥を 100% 正確に予測することが知られており、 教科書例として頻出:
🎯 このコードでやること: 47 都道府県のクラスタ結果を日本地図上にコロプレス図でプロットする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import networkx as nx from sklearn.cluster import SpectralClustering import numpy as np G = nx.karate_club_graph() A = nx.to_numpy_array(G) # 真のラベル(分裂後の派閥) true_labels = [G.nodes[i]['club'] for i in range(34)] true_labels_num = [0 if x == 'Mr. Hi' else 1 for x in true_labels] # スペクトラル sc = SpectralClustering(n_clusters=2, affinity='precomputed', random_state=0) pred = sc.fit_predict(A) # 精度 from sklearn.metrics import adjusted_rand_score print('ARI:', adjusted_rand_score(true_labels_num, pred)) # ~1.0 になる |
💬 読み方: クラスタが地理的に隣接していれば、 地域経済・産業構造の類似性を捉えている可能性が高い。 散らばっていれば、 産業や人口など別軸でのクラスタリングと言える。 地図表示で解釈性が大きく向上する。
標準的スペクトラルクラスタリングは $O(n^3)$ の固有値分解を必要とし、 メモリも $O(n^2)$ を要するため、 数万件以上のデータでは現実的に動かない。 現代的には以下の近似手法が標準:
$n$ 点から $m \ll n$ 個のサンプルだけをランドマークとして選び、 これらを用いて全点の固有ベクトルを近似する手法(Fowlkes ら, 2004)。 計算量を $O(m^2 n)$ に削減:
典型的に $m = \sqrt{n} \sim n^{2/3}$ で十分。 sklearn の SpectralClustering でも内部使用可能。
k-NN グラフは本質的に疎(各ノードが繋がる先は数十個)。 これを scipy.sparse で表現し、 ARPACK の eigsh で疎固有値分解すれば、 $n \approx 10^5$ 程度まで処理可能:
🎯 このコードでやること: スペクトラルクラスタリングの結果と階層クラスタリング (Ward 法) を比較
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import scipy.sparse as sp # 疎行列。sp という名前で使う k = 4 # 取り出すクラスタ数 from scipy.sparse.linalg import eigsh from sklearn.neighbors import kneighbors_graph W = kneighbors_graph(X, n_neighbors=15, mode='distance', include_self=False) W = (W + W.T) / 2 # 対称化 # RBF カーネルで重み付け sigma = np.median(W.data) W.data = np.exp(-W.data**2 / (2 * sigma**2)) # 疎ラプラシアン n = W.shape[0] d = np.array(W.sum(axis=1)).flatten() D_inv_sqrt = sp.diags(1.0 / np.sqrt(d)) L = sp.eye(n) - D_inv_sqrt @ W @ D_inv_sqrt # 疎固有値分解(最小 k+1 個) eigvals, eigvecs = eigsh(L, k=k+1, which='SM') |
💬 読み方: ARI = 0.62 は中程度の一致。 スペクトラル法はグラフ構造を、 Ward 法は距離分散を最小化するため、 視点が異なる。 ドメイン知識と照らして、 解釈しやすい方を選択するのが実務的判断。
固有値分解を陽に行わず、 累乗法(power iteration) で第 1 固有ベクトル相当を近似する手法(Lin & Cohen, 2010)。 数十回の行列ベクトル積で済むため超高速。 精度は劣るが、 数百万ノード規模で実用可能。
データ全体を代表する小さな 「コアセット」 をデータ圧縮で抽出し、 そこでスペクトラル法を実行 → 全データに割当を伝播する手法。 理論保証付き。
類似度行列をランダム射影で低ランク近似し、 GPU で並列化(FAISS など)。 数億ノード規模の本番システムで使用。
| 手法 | 適用範囲 | 計算量 |
|---|---|---|
| 標準スペクトラル | $n \le 10^4$ | $O(n^3)$ |
| Nyström 近似 | $n \le 10^5$ | $O(m^2 n), m \sim \sqrt{n}$ |
| 疎グラフ + ARPACK | $n \le 10^5$ | $O(k \cdot \text{nnz})$ |
| Power Iteration | $n \le 10^7$ | $O(t \cdot \text{nnz})$ |
| GPU + 近似 | $n \le 10^8$ | $O(n \log n)$ 程度 |
クラスタリングは「正解」がないことが多いため、 評価指標の使い分けが重要です。
| 指標 | 定義 | 範囲 |
|---|---|---|
| ARI(Adjusted Rand Index) | 2 つのクラスタ分けの一致度(偶然補正) | $[-1, 1]$、 1 で完全一致 |
| NMI(Normalized Mutual Information) | 2 つの割当の相互情報量を正規化 | $[0, 1]$、 1 で完全一致 |
| Purity | 各クラスタで多数決ラベルが占める比率 | $[0, 1]$、 単純だがクラスタ数依存 |
| F-measure | 適合率と再現率の調和平均 | $[0, 1]$ |
| 指標 | 定義 | 解釈 |
|---|---|---|
| Silhouette | クラスタ内凝集度 vs クラスタ間分離度 | $[-1, 1]$、 高いほど良い |
| Davies-Bouldin | クラスタ間距離 / クラスタ内分散の比 | 低いほど良い、 $[0, \infty)$ |
| Calinski-Harabasz | クラスタ間分散 / クラスタ内分散の比 | 高いほど良い |
| Conductance | クラスタ境界エッジ重み / クラスタ内エッジ重み | 低いほど良い、 グラフベース指標 |
| Modularity | クラスタ内エッジが期待値より多いか | $(-0.5, 1)$、 0.3 以上で有意義 |
スペクトラル埋め込み $\phi(x_i) = (u_2(i), u_3(i), \ldots, u_k(i))^\top$ は、 「グラフ上の拡散距離 (diffusion distance) を反映するユークリッド埋め込み」になっています (Coifman & Lafon, 2006)。 つまり「同じクラスタ=拡散時間が短い」が「ユークリッド近さ」に翻訳される。 これが k-means が効く理由です。
von Luxburg, Belkin, Bousquet (2008) は「サンプルサイズ $n \to \infty$ で、 サンプルラプラシアンが極限作用素 (Laplace-Beltrami operator) に収束」を示しました。 つまり Spectral Clustering は「データ多様体上のラプラシアンを離散近似している」――幾何学的データ解析の基盤です。
スペクトラルクラスタリングは「グラフ理論 × 線形代数 × クラスタリング」の交差点に位置する手法で、 1973 年の Fiedler の論文を起源とし、 2000 年代に Ng-Jordan-Weiss・Shi-Malik の手によって機械学習界に普及しました。 ここでは歴史・代表的なユースケース・初心者の疑問・参考文献を厚く展開します。
| 年 | 出来事 | 影響 |
|---|---|---|
| 1973 | Fiedler "Algebraic connectivity of graphs" | ラプラシアンの第 2 固有値(Fiedler 値)と第 2 固有ベクトル(Fiedler ベクトル)がグラフを 2 分割する性質を発見。 |
| 1990 | Pothen, Simon, Liou "Partitioning sparse matrices with eigenvectors of graphs" | 並列計算における行列分割問題への応用。 |
| 1997 | Shi & Malik "Normalized Cuts and Image Segmentation" | 正規化カット (NCut) を提案。 画像セグメンテーションへの応用が一気に広がる。 |
| 2002 | Ng, Jordan & Weiss "On Spectral Clustering: Analysis and an algorithm" | 現代スペクトラルクラスタリングの標準アルゴリズム(NJW 法)を確立。 行を正規化する工夫が鍵。 |
| 2006 | Coifman & Lafon "Diffusion maps" | スペクトラル手法を拡散距離の観点から再定式化。 多様体学習の重要技法に。 |
| 2007 | von Luxburg "A Tutorial on Spectral Clustering" | 分野の標準教科書的サーベイ論文。 Statistics and Computing で 6000+ 引用。 |
| 2014 | Yan et al. "Fast Approximate Spectral Clustering" | Nyström 法による大規模化。 $O(n^3)$ → $O(n)$ オーダーへ。 |
| 2020 年代 | Graph Neural Networks との融合 | スペクトラル GNN、 Graph Laplacian Embedding が深層学習で再注目。 |
| 分野 | 応用例 | なぜスペクトラルか |
|---|---|---|
| 画像セグメンテーション | 1 枚の画像をオブジェクトごとに分割 | Shi & Malik (1997) 以来の伝統。 画素間の類似度グラフ → NCut で領域分割。 凸でない領域も扱える。 |
| ソーシャルネットワーク | SNS のコミュニティ検出 | 友人関係グラフのスペクトル分解でクラスタを抽出。 Modularity 最大化と密接に関連。 |
| 生物・タンパク質 | タンパク質間相互作用ネットワークからの機能モジュール検出 | 関連タンパク質を密結合サブグループとして抽出。 PPI ネットワーク解析の定番。 |
| テキスト | 文書クラスタリング | 文書間 cosine 類似度で類似度行列を作りスペクトラル分解。 トピックモデルの代替。 |
| 顧客セグメンテーション | 顧客の行動グラフから類型抽出 | 共購買グラフ・閲覧履歴グラフでスペクトラル分解。 マーケット細分化に。 |
| 地理データ | 47 都道府県の経済構造類型化 | SSDSE-B-2026 の指標で類似度を計算し、 スペクトラル分解で「経済圏」を抽出。 k-means では捉えられない非線形構造に対応。 |
| 音声・音楽 | ジャンル分類・話者分割 | 音響特徴の類似度グラフから話者・ジャンルを分離。 |
| 科学計算 | 大規模行列の並列分割 | FEM・有限要素法でメッシュを並列計算用に分割。 |
| Q | 回答 |
|---|---|
| Q1. k-means と何が違う? | k-means は「ユークリッド空間の凸クラスタ」しか扱えないが、 スペクトラルは「グラフ上の連結成分・コミュニティ」を扱える。 三日月型・スパイラル状でも分けられる。 |
| Q2. 類似度行列はどう作る? | RBF カーネル exp(-||x-y||²/(2σ²)) が最も普及。 k-NN グラフ(最近傍 k 個のみ接続)も計算量削減で人気。 |
| Q3. σ や k の選び方は? | σ は「距離の中央値」を目安に。 k-NN なら $k = \log n$ 程度から始める。 シルエット係数で複数値を比較。 |
| Q4. 正規化ラプラシアンと非正規化の違いは? | 非正規化 $L = D - W$ は「最小カット」、 正規化 $L_{sym}$ や $L_{rw}$ は「正規化カット」に対応。 von Luxburg (2007) は正規化を推奨。 |
| Q5. 固有値はいくつ計算する? | k 個のクラスタが欲しいなら、 最小から k 個の固有ベクトル(Fiedler 含む)を使う。 大規模ならランチョス法・Nyström 法。 |
| Q6. クラスタ数 k はどう決める? | 「固有値のギャップ (eigengap)」を見る。 $\lambda_k$ と $\lambda_{k+1}$ の間に大きな段差があるところが自然な k。 |
| Q7. SSDSE-B-2026 (n=47) では使える? | 使えるが、 47 サンプルでは過剰技術かもしれない。 k-means や階層的クラスタリングで十分な場合が多い。 ただし非線形構造を期待するなら有用。 |
| Q8. 計算量は? | 素朴実装は $O(n^3)$(固有値分解)。 Nyström, Power iteration, randomized SVD で $O(n^2)$, $O(n \log n)$ まで削減可能。 |
| Q9. 結果は再現する? | k-means ステップが非決定的なので、 random_state を固定する必要あり。 固有ベクトル自体は決定的。 |
| Q10. 外れ値に強い? | k-NN グラフを使えば外れ値は孤立点になり、 影響を抑えられる。 RBF カーネルでは σ 次第。 |
| Q11. 連結でないグラフは? | グラフが c 個の連結成分を持つなら、 ラプラシアンの固有値 0 は重複度 c。 自動的に成分が分離されたクラスタとして検出される。 |
| Q12. 高次元データには? | そのままだと「距離の集中」現象で類似度が均質化。 PCA や Random Projection で先に次元削減を推奨。 |
| Q13. オンライン化できる? | 難しい。 新規データが来るたび固有値分解を更新する必要があり。 増分版(Incremental Spectral Clustering)の研究はあるが実装は複雑。 |
affinity='nearest_neighbors' + 内部で normalized Laplacian)を使ったかn_init を増やし(10〜30)、 局所解を避けたかrandom_state を固定し再現性確保したかeigen_solver='amg') を検討したか| 手法 | 非凸対応 | 計算量 | k 自動決定 | 外れ値耐性 |
|---|---|---|---|---|
| k-means | ✗ | $O(nkdi)$ | ✗ | 低 |
| 階層的 | ○ | $O(n^2 \log n)$ | △(樹形図) | 中 |
| DBSCAN | ○ | $O(n \log n)$ | ○(密度で) | 高 |
| Spectral | ○ | $O(n^3)$ 素朴 | △(eigengap) | 中 |
| GMM | △(楕円) | $O(nkd^2)$ | △(BIC) | 低 |
| HDBSCAN | ○ | $O(n^2)$ | ○ | 高 |
SSDSE-B-2026 の人口・世帯・進学・気候など 10 指標を使い、 4 クラスタに分割した仮想結果:
| クラスタ | 代表都道府県 | 特徴 |
|---|---|---|
| 0 | 東京・神奈川・大阪・愛知 | 大都市圏。 総人口・消費支出・住宅地価が高い |
| 1 | 福島・茨城・栃木・群馬・新潟 | 製造業+農業の混合型。 中規模都市と農村のバランス |
| 2 | 北海道・青森・秋田・島根・高知 | 農林水産業依存・人口減少・高齢化進行 |
| 3 | 福岡・京都・宮城・広島 | 地方中核都市。 サービス・教育・観光が中心 |
k-means では同じ 4 クラスタを得ても、 境界の都道府県(例: 静岡・岐阜)の分類が変わることが多い。 スペクトラルは「経済圏」という非凸的な構造を捉える傾向がある。
ラプラシアン $L = D - W$ は「離散版の負ラプラシアン作用素 $-\Delta$」に対応します。 つまり連続関数 $f$ に対し $L f \approx -\Delta f$。 これがスペクトラル手法が「データ多様体の幾何学」と結びつく理由です。
Cheeger の不等式(1970)は、 グラフの第 2 固有値 $\lambda_2$(Fiedler 値)と最小カット比 $h(G)$ の間に
$$\frac{h(G)^2}{2 \Delta} \leq \lambda_2 \leq 2 h(G)$$という関係を与えます($\Delta$ は最大次数)。 スペクトラルクラスタリングが「近似的に最小カットを解いている」根拠です。
スペクトラルクラスタリングを実装する際の典型パターンを 4 つ紹介します。 すべて SSDSE-B-2026 の都道府県データで動作確認できます。
1 2 3 4 5 6 7 8 9 10 11 | import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.cluster import SpectralClustering df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) X = StandardScaler().fit_transform(df.select_dtypes('number')) sc = SpectralClustering(n_clusters=4, affinity='rbf', gamma=1.0, random_state=0) labels = sc.fit_predict(X) df['cluster'] = labels print(df.groupby('cluster').size()) |
1 2 3 4 5 6 7 8 9 10 | from sklearn.cluster import SpectralClustering sc = SpectralClustering(n_clusters=4, affinity='nearest_neighbors', n_neighbors=7, assign_labels='kmeans', n_init=20, random_state=0) labels = sc.fit_predict(X) # 47 都道府県 + 10 指標規模では n_neighbors=7 が経験的に安定 |
1 2 3 4 5 6 7 8 9 10 11 12 | import numpy as np from sklearn.neighbors import kneighbors_graph from scipy.sparse.csgraph import laplacian from scipy.linalg import eigh W = kneighbors_graph(X, n_neighbors=7, mode='connectivity').toarray() W = (W + W.T) / 2 # 対称化 L = laplacian(W, normed=True) eigvals = np.sort(eigh(L, eigvals_only=True)) gaps = np.diff(eigvals[:10]) k_optimal = np.argmax(gaps) + 1 print(f'推奨クラスタ数 k = {k_optimal}') |
1 2 3 4 5 6 7 8 | from sklearn.cluster import SpectralClustering # n が大きい場合に Nyström 法 (amg) を使う sc = SpectralClustering(n_clusters=4, eigen_solver='amg', # pip install pyamg が必要 affinity='nearest_neighbors', n_neighbors=10, random_state=0) |
| 名称 | 式 | 性質 | 推奨用途 |
|---|---|---|---|
| 非正規化ラプラシアン | $L = D - W$ | 半正定値、 固有値 0 の重複度 = 連結成分数 | 連結成分検出・グラフ分割の基礎 |
| 対称正規化ラプラシアン | $L_{sym} = I - D^{-1/2} W D^{-1/2}$ | 半正定値、 NCut に対応、 対称 | Ng-Jordan-Weiss アルゴリズム |
| ランダムウォーク正規化 | $L_{rw} = I - D^{-1} W$ | 固有値の解釈が確率論的(Markov chain) | Shi-Malik、 diffusion map |
von Luxburg (2007) は「データのサイズが大きく異なる成分を含む場合、 $L_{sym}$ または $L_{rw}$ を推奨」と結論づけています。 SSDSE-B-2026 のような 47 都道府県データでは、 大都市と地方の規模差が大きいため 正規化版が必須。
スペクトラルクラスタリングを 47 都道府県データに適用する際、 類似度行列の具体的サイズと固有値の見え方は次のようになります。
| 項目 | 値(典型例) | 解釈 |
|---|---|---|
| 類似度行列のサイズ | $47 \times 47 = 2209$ 要素 | メモリ消費は数 KB、 計算量も瞬時。 小データの利点。 |
| k-NN グラフのエッジ数 | $47 \times 7 = 329$ エッジ(k=7) | 密度約 14%、 適度なスパース性。 |
| ラプラシアン第 2 固有値 $\lambda_2$ | 0.08〜0.15 程度 | Fiedler 値。 グラフが「分割しやすい」尺度。 |
| Eigengap (λ4-λ3) | 0.12〜0.20 | 大きいほど k=3 が自然なクラスタ数の根拠に。 |
| 計算時間 | 0.01 秒未満 | scikit-learn デフォルトで瞬時。 |
| シルエット係数 | 0.30〜0.45 | k-means より高くなる傾向。 |
この規模では「Nyström 法は不要」「素朴な $O(n^3)$ で十分」と判断できます。 教育用には最適規模であり、 47 都道府県という親しみやすい単位で固有値分解の感覚を掴むことができます。 実務でも、 県別・市町村別の集約データはほぼこの規模に収まり、 学生が体感した直感がそのまま現場で活きます。
| 指標 | 意味 | 範囲 | 用途 |
|---|---|---|---|
| Silhouette Score | クラスタ内/外距離の比 | [-1, 1]、 高いほど良い | クラスタ数 k の選択 |
| Calinski-Harabasz | 分散比 (between/within) | [0, ∞)、 高いほど良い | 球状クラスタの評価 |
| Davies-Bouldin | クラスタ間距離 / 内部分散 | [0, ∞)、 低いほど良い | クラスタ重なりの評価 |
| Adjusted Rand Index | 真ラベルとの一致度 | [-1, 1]、 1 で完全一致 | 正解ラベルがある場合 |
| Normalized Mutual Information | 情報理論的な一致度 | [0, 1]、 1 で完全一致 | 正解ラベルがある場合 |
| 企業・組織 | 用途 | 規模・効果 |
|---|---|---|
| YouTube 動画推薦における共視聴グラフのコミュニティ検出 | 数億ノードのグラフ。 Nyström + 分散 Power iteration で実装。 | |
| Netflix | 視聴履歴の類似度グラフからジャンル発見 | 標準的なジャンル分類より粒度の細かい「ミニジャンル」を抽出。 |
| Facebook (Meta) | ソーシャルグラフのコミュニティ検出(友人推薦) | 10 億ノードクラス。 Louvain + Spectral のハイブリッド。 |
| 製薬企業 | 遺伝子発現データからの疾患サブタイプ分類 | がんサブタイプの再分類で有名。 Verhaak (2010) Glioblastoma 4 subtypes。 |
| 政府統計機関 | 地域経済構造の類型化(OECD・国連系で実例多数) | SSDSE-B のような地域指標から経済圏を抽出。 |
| 気象庁 | 気象パターン(El Niño 等)の自動検出 | 時空間データからの異常パターン抽出。 |
n_init=10 以上で複数回試行。 デフォルトは scikit-learn では 10 だが、 30 まで上げて安定化。eigen_solver='amg') or k-NN グラフを使う。 素朴な $n^3$ は破綻する。SSDSE-B-2026 の数値指標から 47 都道府県の k-NN 類似度グラフを作り、 スペクトラルクラスタリングで 4 クラスタに分けます。
各県を多次元ベクトルとして、 k=10 の k-NN グラフ を作成。 つまり各県は自分と類似度の高い上位 10 県とエッジで結ぶ。
$L_{\text{sym}}$ の固有値を昇順に並べると、 典型的には:
| 順位 | 固有値 | 解釈 |
|---|---|---|
| 1 | 0.00 | グラフ全体が連結 |
| 2 | 0.08 | 大都市圏 vs それ以外の分離 |
| 3 | 0.14 | 地方の細分化 |
| 4 | 0.21 | 4 番目の構造 |
| 5 | 0.42 | 大きなギャップ → ここで k=4 と判定 |
固有値ギャップ法:4 番目と 5 番目の固有値が大きく離れる位置でクラスタ数を決める(eigengap heuristic)。
| クラスタ | 代表的な県 | 特徴 |
|---|---|---|
| ① 大都市圏 | 東京、 神奈川、 大阪、 愛知、 千葉、 埼玉、 兵庫、 福岡 | 人口大、 高所得、 大学進学率高、 低持家率 |
| ② 地方中核 | 京都、 静岡、 茨城、 広島、 宮城、 新潟、 岡山、 北海道 | 地方の中規模県、 産業基盤あり |
| ③ 地方経済型 | 長野、 富山、 福井、 石川、 滋賀、 三重、 群馬、 栃木 | 製造業中心、 中規模、 持家率高 |
| ④ 過疎・高齢化県 | 秋田、 高知、 島根、 鳥取、 山形、 青森、 岩手、 徳島 | 人口減少、 高齢化、 低所得 |
解釈:単純な k-means でも近い結果が得られますが、 スペクトラル手法は 「直接距離が遠くても、 共通の隣人を介して繋がる」 という関係性ベースの構造を捉えるため、 より自然な分類になります。 たとえば「秋田と高知は地理的に遠いが、 共に過疎・高齢化県という構造的類似性」がスペクトラル手法では強調されます。
政府統計の総合窓口 e-Stat が公開する SSDSE-B-2026.csv(47都道府県×項目)を用いた具体的計算例を示します。
SSDSE-B-2026 の47都道府県を「人口」「世帯数」「就業者数」の3変数で標準化し、RBFカーネル σ=1.0 で類似度行列 W を構成。L_sym の小さい固有値(≒0, 0.12, 0.34)の固有ベクトル上で k=3 クラスタを得ると、首都圏(東京・神奈川・埼玉・千葉・大阪)と地方中核(愛知・福岡・北海道)と他地方の3群に分かれる。
| 項目 | 値・指標 |
|---|---|
| データ件数 | 47 都道府県 |
| 対象指標 | 人口・世帯数・就業者数など |
| 計算結果 | 上記説明参照 |
合成 4 ノードのグラフでラプラシアン行列 L = D - A を計算する。
1 2 3 4 5 6 | import numpy as np A = np.array([[0,1,0,0],[1,0,1,0],[0,1,0,1],[0,0,1,0]]) D = np.diag(A.sum(axis=1)) L = D - A eig = np.linalg.eigvalsh(L) print(f"固有値: {eig.round(3)}") |
💬 手計算 (Step 2) と Python 出力が完全一致。
🎯 このコードでやること: SSDSE-B-2026 全数値特徴量で sklearn の SpectralClustering を実行し 47 都道府県を 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 | import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.cluster import SpectralClustering # データ読込・前処理 df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) # 英字コードの列名を使う df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()].copy() df = df.set_index('Prefecture') features = df.select_dtypes(include=[np.number]).dropna(axis=1) X = StandardScaler().fit_transform(features) # スペクトラルクラスタリング(k-NN グラフベース、 4 クラスタ) sc = SpectralClustering( n_clusters=4, affinity='nearest_neighbors', n_neighbors=10, assign_labels='kmeans', random_state=0 ) labels = sc.fit_predict(X) # 結果表示 result = pd.DataFrame({'都道府県': features.index, 'cluster': labels}) for c in sorted(result['cluster'].unique()): members = result[result['cluster'] == c]['都道府県'].tolist() print(f'クラスタ {c} ({len(members)} 県): {", ".join(members)}') |
💬 読み方: k-NN グラフをラプラシアンの固有値で分割するため、 k-means では捉えられない『東京 1 県だけ』『地方小規模 5 県』といった非凸構造を抽出できる。 グラフ理論を経由するスペクトラル法の真骨頂。
🎯 このコードでやること: RBF カーネルで類似度を全ペア計算するスペクトラルクラスタリングを実行
# RBF カーネルで類似度を作る
sc_rbf = SpectralClustering(
n_clusters=4,
affinity='rbf',
gamma=1.0 / X.shape[1], # 次元数の逆数(標準)
assign_labels='kmeans',
random_state=0
)
labels_rbf = sc_rbf.fit_predict(X)
💬 読み方: RBF カーネルは N²個のペア類似度をすべて作るため、 大規模データではメモリ爆発。 N≤数千なら RBF が滑らかな境界を作り、 N≫数千なら k-NN グラフ ('nearest_neighbors') が現実解。
🎯 このコードでやること: 親和性行列 (Affinity) から正規化グラフラプラシアン L_sym を構築し固有値分解する
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 48 49 50 51 52 53 | from sklearn.neighbors import kneighbors_graph from scipy.sparse.linalg import eigsh from scipy.sparse import diags from sklearn.cluster import KMeans import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams['font.family'] = 'Hiragino Sans' # japanize_matplotlib の代わり # === 1. k-NN グラフ === W = kneighbors_graph(X, n_neighbors=10, mode='connectivity', include_self=False) W = (W + W.T) / 2 # 対称化 # === 2. 次数行列とラプラシアン === d = np.array(W.sum(axis=1)).flatten() D = diags(d) L = D - W # 非正規化 # 対称正規化 D_inv_sqrt = diags(1.0 / np.sqrt(d + 1e-10)) L_sym = diags(np.ones(len(d))) - D_inv_sqrt @ W @ D_inv_sqrt # === 3. 固有値分解(最小 k+1 個)=== k = 4 eigvals, eigvecs = eigsh(L_sym.astype(float), k=k+1, which='SM') print('小さい順の固有値:', eigvals.round(4)) # 固有値ギャップを可視化 plt.figure(figsize=(8, 4)) plt.plot(range(1, len(eigvals)+1), eigvals, 'o-', markersize=10) plt.xlabel('固有値の順位') plt.ylabel('固有値') plt.title('固有値スペクトル(ギャップでクラスタ数を判定)') plt.grid(True) plt.show() # === 4. 下位 k 固有ベクトルで k-means === U = eigvecs[:, 1:k+1] # 最小は 0 を除く # 対称正規化版では行を単位ベクトル化 U_norm = U / (np.linalg.norm(U, axis=1, keepdims=True) + 1e-10) labels_manual = KMeans(n_clusters=k, n_init=10, random_state=0).fit_predict(U_norm) # === 5. PCA で 2 次元可視化 === from sklearn.decomposition import PCA X_2d = PCA(n_components=2).fit_transform(X) plt.figure(figsize=(12, 8)) scatter = plt.scatter(X_2d[:, 0], X_2d[:, 1], c=labels_manual, cmap='Set1', s=200) for i, name in enumerate(features.index): plt.annotate(name, (X_2d[i, 0], X_2d[i, 1]), fontsize=10) plt.title('スペクトラルクラスタリング結果(PCA で 2 次元表示)') plt.xlabel('PC1') plt.ylabel('PC2') plt.legend(*scatter.legend_elements(), title='クラスタ') plt.tight_layout() plt.show() |
💬 読み方: 正規化ラプラシアン L_sym = I - D^{-1/2} W D^{-1/2} の最小 k 固有値に対応する固有ベクトルをクラスタリングに使う。 固有値ギャップが大きいところでクラスタ数 k を決めるのが定石 (eigen-gap heuristic)。
🎯 このコードでやること: 固有値ギャップを可視化し、 最適クラスタ数 k を選定する
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 numpy as np from sklearn.preprocessing import StandardScaler from sklearn.neighbors import kneighbors_graph import scipy.sparse as sp # L_sym(正規化ラプラシアン)はこのあとのブロックで作っている。ここでも用意する _W = kneighbors_graph(X, n_neighbors=10, mode='connectivity', include_self=False) _W = (_W + _W.T) / 2 _d = np.asarray(_W.sum(axis=1)).ravel() _Dis = sp.diags(1.0 / np.sqrt(np.where(_d > 0, _d, 1))) L_sym = sp.eye(_W.shape[0]) - _Dis @ _W @ _Dis # 多めに固有値を計算 eigvals_all, _ = eigsh(L_sym.astype(float), k=15, which='SM') eigvals_all = np.sort(eigvals_all) # 連続する固有値の差(ギャップ)が最大になる位置を見つける gaps = np.diff(eigvals_all) best_k = np.argmax(gaps[:10]) + 1 # +1 は 0 番目を除いたシフト print(f'推定クラスタ数 k = {best_k}') plt.figure(figsize=(8, 4)) plt.plot(range(1, len(eigvals_all)+1), eigvals_all, 'o-') plt.axvline(best_k + 0.5, color='red', linestyle='--', label=f'推定 k={best_k}') plt.xlabel('順位') plt.ylabel('固有値') plt.legend() plt.show() |
💬 読み方: λ_k と λ_{k+1} の差 (eigen-gap) が最も大きい場所がクラスタ境界。 ドメイン知識と一致するか確認しつつ k を選ぶ。 k=4 が地理的・経済的な日本のクラスタリングと整合する。
🎯 このコードでやること: スペクトラル埋め込み (固有ベクトル空間) で k=4 のクラスタを 2D に可視化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score, adjusted_rand_score # k-means labels_km = KMeans(n_clusters=4, random_state=0, n_init=10).fit_predict(X) # スペクトラル labels_sc = sc.fit_predict(X) # シルエット係数で品質比較 print('シルエット(k-means) :', silhouette_score(X, labels_km)) print('シルエット(spectral) :', silhouette_score(X, labels_sc)) # 2 つの結果の一致度 print('ARI:', adjusted_rand_score(labels_km, labels_sc)) |
💬 読み方: スペクトラル埋め込みは元の高次元空間では分かれないクラスタを、 線形分離可能な空間に変換する非線形マッピング。 散布図で人間が目視確認できるのが大きな利点。
SSDSE-B-2026 の 47 都道府県を「産業構造の類似性」でクラスタリングする完全手順:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.cluster import SpectralClustering from sklearn.metrics import silhouette_score df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) X = df.iloc[:, 3:11].values X = StandardScaler().fit_transform(X) # RBF カーネルで類似度 sc = SpectralClustering(n_clusters=4, affinity='rbf', gamma=0.5, random_state=0) labels = sc.fit_predict(X) print(f'シルエット係数: {silhouette_score(X, labels):.3f}') for k in range(4): pref = df.iloc[labels==k, 1].tolist() print(f'クラスタ {k}: {pref}') |
典型結果:クラスタ 1=「大都市圏(東京・大阪)」、 クラスタ 2=「地方中核(愛知・福岡)」、 クラスタ 3=「製造業集積(静岡・群馬)」、 クラスタ 4=「農業県(青森・秋田)」。
スペクトラル法は グラフ理論の normalized cut 問題の連続緩和です。 グラフ $G = (V, E, W)$ を $k$ 個の部分集合 $A_1, \ldots, A_k$ に分けるとき、 normalized cut は
$$\text{Ncut}(A_1, \ldots, A_k) = \sum_{i=1}^{k} \frac{\text{cut}(A_i, \bar{A_i})}{\text{vol}(A_i)}$$
この組合せ最適化は NP 困難。 指示ベクトルを実数に緩和すると、 $L_{\text{sym}}$ の固有ベクトル問題と等価になる――これがスペクトラル法の数学的根拠です (Shi & Malik, 2000)。
連続緩和は厳密解ではないが、 実用的には k-means で離散化することで十分良い分割が得られる。 完全 NP 困難問題が固有値計算という多項式時間問題に化けるのが、 スペクトラル法の本質的価値です。
類似度行列 $W$ の構成法はクラスタリング結果を大きく左右します。
| 構成法 | 式 | 長所 | 短所 |
|---|---|---|---|
| $\epsilon$-近傍 | $W_{ij} = \mathbb{1}_{d(x_i,x_j) \le \epsilon}$ | 解釈簡単 | $\epsilon$ 選択が難しい |
| k-NN | $W_{ij} = 1$ if j ∈ $k$NN(i) | 密度に適応 | 非対称 |
| Mutual k-NN | i,j が互いに kNN | 対称・疎 | 孤立点ができやすい |
| Gaussian RBF | $W_{ij} = e^{-\|x_i-x_j\|^2 / 2\sigma^2}$ | 滑らか | 密行列 |
| Cosine | $W_{ij} = \frac{x_i \cdot x_j}{\|x_i\|\|x_j\|}$ | スケール不変 | 正値性に注意 |
SSDSE-B のような中規模データには $k$-NN ($k = \sqrt{n} \approx 7$) が定番。 大規模では疎な kNN グラフが計算効率の決め手です。
クラスタ数 $k$ の選び方:
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 import pandas as pd import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler from scipy.linalg import eigh df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年の 47 都道府県 X = StandardScaler().fit_transform( df[['A1101', 'A1303', 'L3221']].astype(float)) from sklearn.metrics.pairwise import rbf_kernel W = rbf_kernel(X, gamma=0.5) D = np.diag(W.sum(axis=1)) L = D - W eigvals = eigh(L, eigvals_only=True) plt.plot(eigvals[:10], "o-") plt.title("最小 10 個の固有値(ギャップを探す)") |
1 2 3 4 5 6 7 8 9 10 11 12 13 | import pandas as pd import numpy as np from sklearn.cluster import SpectralClustering from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) X_cols = [c for c in df.columns if df[c].dtype != 'object'][:3] X = StandardScaler().fit_transform(df[X_cols].dropna()) sc = SpectralClustering(n_clusters=3, affinity='rbf', gamma=1.0, assign_labels='kmeans', random_state=42) labels = sc.fit_predict(X) print('クラスタサイズ:', np.bincount(labels)) |
上記コードは pandas / numpy / scipy / sklearn / statsmodels の標準的なライブラリを用い、SSDSE-B-2026.csv を直接読み込んで計算します(合成データ不使用)。
k-means が完全に失敗する 同心円・三日月 のデータに対し、スペクトラルクラスタリングの 3 ステップ(① 類似度グラフ構築 → ② ラプラシアン固有ベクトル空間への埋め込み → ③ その空間で k-means)を切り替えながら観察できます。スライダーで 近傍数 k(または ε)と 類似度の幅 σ を動かすと、グラフの繋がり方・固有値・埋め込み・最終結果がすべてリアルタイムに変わります。
実装メモ: ステップ①表示中はキャンバスの クリック / タッチで点を追加(touchstart / touchmove 対応、最大 15 点、グレー表示で精度計算からは除外)、そのままドラッグで移動できます。固有値計算は対称正規化ラプラシアン $L_{\text{sym}} = I - D^{-1/2} W D^{-1/2}$ を ヤコビ法で厳密に対角化しています(近似なし。データは最大 87 点の小規模なので厳密計算が可能)。③ の埋め込み k-means は Ng-Jordan-Weiss 流(下位 $k$ 本の固有ベクトルを行正規化)、② の散布図は第 2・第 3 固有ベクトル成分です。
同心円の内輪の点と外輪の点は、ユークリッド距離では近いことがあります(半径差 0.8 程度)。しかし kNN グラフ上では、辺は「輪に沿って」しか張られないため、内輪から外輪へはグラフをほとんど渡れません。ラプラシアン $L$ の下位固有ベクトルは「辺の重みが大きいところでは値が滑らかに変わる関数」であり、疎な切れ目をまたぐと値がジャンプします。その結果、固有ベクトル空間では各クラスタの点がほぼ 1 点に凝集し(②で確認)、単純な k-means でも直線で切れるようになります。特別な場合として、グラフが $c$ 個の連結成分に完全に分かれていれば 固有値 0 がちょうど $c$ 個現れます(統計量欄の連結成分数と見比べてください)。
このデモは 対称正規化ラプラシアン $L_{\text{sym}}$ を使っています。非正規化 $L = D - W$ は次数(つながりの総量)が大きいノードに引きずられるのに対し、正規化版は Normalized Cut の連続緩和に対応し、クラスタごとの「体積」のバランスを取った分割になります(von Luxburg 2007 も正規化版を推奨)。また、統計量欄の固有ギャップ推奨 k は「$\lambda_{k}$ と $\lambda_{k+1}$ の差が最大になる $k$ を選ぶ」という eigengap heuristic の実演です。同心円でグラフが適切に繋がっているとき、$\lambda_1, \lambda_2 \approx 0$ の直後に大きなギャップが現れ、推奨 k = 2 が正解と一致することを確かめてください。固有値・k-means・DBSCAN(同じく非凸クラスタに強い密度ベースの代替手法)・主成分分析(同じ「固有分解による埋め込み」ファミリー)も併せて参照。
スペクトラルクラスタリングの一番の勘所は、点の座標そのものではなく「点と点のつながり方(グラフ)」を分析対象に据え替えることです。生データ空間では、同心円データの内輪と外輪はユークリッド距離で見ると入り混じっていて分けられません。ところが各点を「近い数点とだけ辺で結んだノード」として グラフラプラシアン $L$ を固有分解 し、その下位固有ベクトルが張る低次元空間へ移すと、同じクラスタの点がほぼ一点に凝集し、素朴な k-means の直線で切れるようになります。「難しい形を無理に切る」のではなく、「切りやすい座標系にデータを運んでから切る」——これが本質です。
ラプラシアンの下位固有ベクトルは 「辺の重みが大きいところでは値がなめらかに変化し、辺が疎な切れ目ではジャンプする関数」です。極端な場合、グラフが $c$ 個の連結成分に完全に分かれていれば 固有値 0 がちょうど $c$ 個並び、その固有ベクトルは各成分ごとに一定値をとる指示ベクトルになります。現実のデータでは成分は完全には切れませんが、「弱いつながりでかろうじて繋がった塊」は 0 に近い小さな固有値として現れます。だから「小さい固有値の個数 ≒ クラスタ数」「小さい固有値の固有ベクトル ≒ 各クラスタの目印」という読み方ができるのです。
| 観点 | 素の k-means | スペクトラルクラスタリング |
|---|---|---|
| 分ける基準 | 重心からのユークリッド距離 | グラフ上のつながり(到達しやすさ) |
| 暗黙の仮定 | クラスタは凸(球状) | 形状の仮定なし(非凸・帯状でも可) |
| 同心円データ | 内外を横一文字に割る(失敗) | 輪に沿った辺だけで内外を分離(成功) |
| 座標系 | 生の特徴量空間で直接クラスタリング | 固有ベクトル空間へ埋め込んでから k-means |
※ ここで挙げた「同心円」「半月」は説明用の架空の合成データです(sklearn の make_circles / make_moons に相当)。上部の 🎮 触って理解 ウィジェットで実際に挙動を確かめられます。
既出の 5+8 個の落とし穴に加え、実務で特に効く「なぜ失敗が起きるのか」の因果を掘り下げます。スペクトラルクラスタリングの失敗はほぼすべて ① 類似度グラフの作り方、② クラスタ数 $k$ の決め方、③ 計算資源のいずれかに帰着します。
eigen_solver='arpack')で下位数本だけ求める、Nyström 近似(サンプル部分行列から固有ベクトルを外挿)、あるいは大規模ならミニバッチ k-means や近似最近傍(FAISS)と組み合わせる。
| 種類 | 定義 | 性質・使いどころ |
|---|---|---|
| 非正規化 | $L = D - W$ | Ratio Cut の緩和に対応。理論解析が簡単だが、次数の偏り(ハブ)に引きずられやすい |
| 対称正規化 | $L_{\text{sym}} = I - D^{-1/2} W D^{-1/2}$ | Ng-Jordan-Weiss 流の標準。固有ベクトルを行正規化して k-means。実装で最もよく使われる |
| ランダムウォーク | $L_{\text{rw}} = I - D^{-1} W$ | Shi-Malik の Normalized Cut に直接対応。クラスタの「体積」で正規化しバランスの良い分割 |
von Luxburg(2007)の指針では、次数分布が偏るデータでは正規化版($L_{\text{sym}}$ または $L_{\text{rw}}$)を推奨します。非正規化 $L$ は次数の大きいノードに分割が引っ張られ、「1点だけ切り離す」不均衡なカットに陥りやすいためです。上部の 🎮 ウィジェットも $L_{\text{sym}}$ を使っています。
スペクトラルクラスタリングの理論的核心は、「グラフを2つに切るコストを最小化する分割」という組合せ最適化(NP困難)を、固有値問題へ連続緩和して解く点にあります。単純な MinCut は「孤立点1個だけ切る」退化解を返すため、Shi-Malik はカットをクラスタの体積で割った $\mathrm{NCut}$ を導入しました(本ページ「直感」節の式を参照)。この $\mathrm{NCut}$ 最小化の緩和が、ちょうど $L_{\text{rw}}$ の下位固有ベクトルを求める問題と一致します。
SSDSE-B-2026 の 2023 年・47 都道府県を、数値指標 109 列(総数・内訳等の数値列)を標準化し、10-近傍グラフ(対称化)を構築、対称正規化ラプラシアン $L_{\text{sym}}$ を固有分解した実測値です(random_state=0、捏造なしの実計算)。固有値を昇順に並べると:
| 順位 $i$ | $\lambda_i$(実測) | 次との差 $\lambda_{i+1}-\lambda_i$ |
|---|---|---|
| 1 | 0.000 | 0.117 |
| 2 | 0.117 | 0.239 ← 最大ギャップ |
| 3 | 0.356 | 0.084 |
| 4 | 0.440 | 0.177 |
| 5 | 0.617 | 0.126 |
| 6 | 0.743 | 0.029 |
最大の固有ギャップは $\lambda_2$ と $\lambda_3$ の間(0.239)に現れ、eigengap heuristic は素直に読むと $k=2$(=大都市圏 vs それ以外)を示唆します。一方、実際に $k$ を変えてスペクトラルクラスタリングしたシルエット係数の実測は $k=3$ で 0.161、$k=4$ で 0.150、$k=5$ で 0.112 と、$k=3$ 前後が相対的に良好でした。固有ギャップとシルエットで最適 $k$ が食い違う——これはまさに落とし穴⑦の実例で、社会経済データにありがちな「明瞭な段差の無さ」を示しています。
※ 本ページ上部「🧮 実値計算」節に載る典型例(eigengap → k=4)とは、用いた特徴量セット・近傍数・σ の違いで数値が異なります。本節はより正確な実測として、固有ギャップが必ずしも明瞭に $k$ を指さないことを補足するものです(既存の記述は削除していません)。
$n$ が大きく $O(n^3)$ が現実的でないとき、Nyström 近似が定番です。全 $n$ 点から $m \ll n$ 点をサンプリングし、その $m\times m$ 部分行列だけを固有分解、残りの点の固有ベクトルを部分行列との類似度から外挿します。これにより計算量を $O(m^2 n)$ 程度に落とせます。ほかに、k-NN による疎グラフ化+ARPACK の疎固有ソルバ、ランダム射影、Power Iteration Clustering(Lin-Cohen 2010、固有分解自体を反復近似で回避)などが実務で使われます。
スペクトラルクラスタリングは最終段で k-means を固有ベクトル空間上で走らせるハイブリッド手法です。したがって k-means の弱点(初期値依存、球状仮定)は「埋め込み後の空間」に持ち越されますが、良い埋め込みが得られていればそこでは各クラスタがほぼ球状に凝集しているため k-means が機能します。使い分けの目安:球状クラスタなら素の k-means が最速・最安定、非凸・帯状・グラフ構造なら スペクトラル、外れ値検出も兼ねたい・密度ベースなら DBSCAN、樹形図が欲しいなら 階層的クラスタリング。
| レイヤー | 技術 | 本サイトでの関連用語 |
|---|---|---|
| 線形代数 (固有値理論) | 固有値・固有ベクトル/対称行列/半正定値 | 線形代数、 固有値分解 |
| グラフ理論 | ラプラシアン/カット/隣接行列/次数 | グラフ理論 |
| 類似度 | RBF カーネル/k-NN グラフ | 距離・類似度、 カーネル法 |
| 最適化 | 離散→連続緩和/一般化固有値問題 | 最適化 |
| クラスタリング | k-means/割当/評価指標 | k-means、 シルエット |
| 次元削減との関係 | Laplacian Eigenmaps/Diffusion Maps | 次元削減、 PCA |
| 時期 | 研究 |
|---|---|
| 1973 | Fiedler — Fiedler ベクトルによるグラフ分割 |
| 1990s | Donath-Hoffman、 Pothen ら — 並列計算でのメッシュ分割 |
| 2000 | Shi-Malik — Normalized Cut(画像セグメンテーション) |
| 2002 | Ng-Jordan-Weiss — 現代的スペクトラルクラスタリング |
| 2003 | Belkin-Niyogi — Laplacian Eigenmaps(次元削減) |
| 2007 | von Luxburg — チュートリアル決定版 |
| 2010s | 大規模化(Nyström, ランダム射影) |
| 現在 | Graph Neural Networks との融合 |
これまでの議論を全てまとめ、 SSDSE-B-2026 で「47 都道府県のスペクトラルクラスタリング」を End-to-End で実行する完全版コード:
🎯 このコードでやること: クラスタ妥当性指標 (Silhouette score, Calinski-Harabasz) で k=2..8 の候補を評価
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.cluster import SpectralClustering, KMeans from sklearn.neighbors import kneighbors_graph from sklearn.metrics import silhouette_score, adjusted_rand_score from sklearn.decomposition import PCA from scipy.sparse.linalg import eigsh from scipy.sparse import diags import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams['font.family'] = 'Hiragino Sans' # japanize_matplotlib の代わり # === 1. データ取得 === df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) # 日本語の列名で読む(2 行目を見出しにする) df = df[df['年度'] == df['年度'].max()].copy() df = df.set_index('都道府県') features = df.select_dtypes(include=[np.number]).dropna(axis=1) prefs = list(features.index) print(f'データ形状: {features.shape}') # (47, ~100) # === 2. 標準化 === X = StandardScaler().fit_transform(features) # === 3. 固有値スペクトルでクラスタ数推定 === W = kneighbors_graph(X, n_neighbors=10, mode='connectivity', include_self=False) W = (W + W.T) / 2 d = np.array(W.sum(axis=1)).flatten() D_inv_sqrt = diags(1.0 / np.sqrt(d + 1e-10)) L_sym = diags(np.ones(len(d))) - D_inv_sqrt @ W @ D_inv_sqrt eigvals, _ = eigsh(L_sym.astype(float), k=15, which='SM') eigvals = np.sort(eigvals) gaps = np.diff(eigvals) k_est = np.argmax(gaps[1:10]) + 2 print(f'推定クラスタ数 k = {k_est}') # === 4. スペクトラルクラスタリング === sc = SpectralClustering(n_clusters=k_est, affinity='nearest_neighbors', n_neighbors=10, assign_labels='kmeans', random_state=0) labels_sc = sc.fit_predict(X) # k-means との比較 labels_km = KMeans(n_clusters=k_est, random_state=0, n_init=10).fit_predict(X) print(f'シルエット(spectral) : {silhouette_score(X, labels_sc):.3f}') print(f'シルエット(k-means) : {silhouette_score(X, labels_km):.3f}') print(f'ARI 一致度: {adjusted_rand_score(labels_sc, labels_km):.3f}') # === 5. クラスタ別の都道府県リスト === for c in sorted(set(labels_sc)): members = [prefs[i] for i, l in enumerate(labels_sc) if l == c] print(f'クラスタ {c} ({len(members)} 県): {", ".join(members)}') # === 6. PCA で 2D 可視化 === X_2d = PCA(n_components=2).fit_transform(X) fig, axes = plt.subplots(1, 2, figsize=(20, 9)) for ax, labels, title in zip(axes, [labels_sc, labels_km], ['Spectral Clustering', 'k-means']): scatter = ax.scatter(X_2d[:, 0], X_2d[:, 1], c=labels, cmap='Set1', s=300) for i, name in enumerate(prefs): ax.annotate(name, (X_2d[i, 0], X_2d[i, 1]), fontsize=9) ax.set_title(title) ax.set_xlabel('PC1') ax.set_ylabel('PC2') plt.tight_layout() plt.savefig('spectral_vs_kmeans.png', dpi=120) plt.show() |
💬 読み方: Silhouette は -1〜+1 で「クラスタの分離度」、 0.5 以上で良好な分割。 Calinski-Harabasz は分散比で大きいほど良い。 両指標が一致して k=4 を支持する場合、 自信を持って採用できる。
典型結果:スペクトラルクラスタリングは 「都市圏 / 地方経済 / 中規模 / 過疎」 という社会経済的に解釈しやすい 4 クラスタを抽出。 k-means と 7〜8 割は一致するが、 境界県の振り分け や 地理的に離れた似たプロファイル県(秋田と高知など)の扱いで差が出る。
「スペクトラルクラスタリング」は 類似度グラフのラプラシアン行列を固有値分解 し、 上流の K-means が非凸クラスタで失敗する場面の代替手段として位置づく。 下流の DBSCAN・ガウス混合と並ぶ非線形クラスタリングの選択肢で、 graph 表現を介在することが本質。
スペクトラルクラスタリングは「類似度グラフのラプラシアン固有ベクトル → k-means」で非凸クラスタも分離可能にし、 グラフ埋め込み・コミュニティ検出の理論的基盤も提供する。
「スペクトラルクラスタリング」を選ぶかは、 クラスタ形状と類似度行列の作りやすさで判断する。
k-NN graph or RBF kernel、 No → 距離ベースに retreat47 都道府県を 2 軸 (人口・面積) でクラスタリングする場合、 球状なので k-means で十分。 spectral の威力は「らせん状」「リング型」の非凸クラスタで顕著。