論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
k-means法
k-Means Clustering
事前に決めた k 個のクラスタ中心からの距離が最小になるように、反復的にクラスタを更新するアルゴリズム。
教師なし学習k-meanskmeansK平均法
📍 文脈💡 30秒結論📖 詳しく🎨 直感図📐 数式🔬 読み解き🧮 計算してみる🎓 深掘り⚠️ 落とし穴🔗 関連用語

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

論文で「k-means クラスタリング」「k=4 でクラスタリングした」「エルボー法で k を決定」のように登場する手法。 47都道府県を「似ている県のグループ」に自動分類するときの定番ツール。

k-means法 とは:事前に決めた k 個のクラスタ中心からの距離が最小になるように、反復的にクラスタを更新するアルゴリズム。

💡 30秒で分かる結論

📖 もっと詳しく

k-means は機械学習の教師なし学習の代表手法。 「事前に決めた k 個のグループに、 似ているサンプル同士を集める」反復アルゴリズム。 1957年に Lloyd が提案し、 1967年に MacQueen が "k-means" と命名。 シンプルかつ高速で、 現在も クラスタリング手法の標準

「means(平均)」が名前の由来:各クラスタの「中心(centroid)」を、 そのクラスタに属するサンプルの平均位置で表すから。 「k 個の中心」を探すアルゴリズム、 と言えます。

k-means は探索的データ分析の万能ツール。 顧客セグメンテーション、 地域類型化、 異常検知、 画像圧縮(色のクラスタリング)まで幅広く使われます。

🎨 直感で掴む

k-means法
47都道府県を「高齢化率×死亡率」の2次元空間で k=4 にクラスタリングした例。 × が各クラスタの中心(centroid)。 右上=高齢化進行、左下=若年層多。

図を見ると、 4色の点群(クラスタ)と各クラスタの × 印(中心)が見えます。 k-meansは「各点が、 自分が属するクラスタ中心に最も近い」状態に収束した結果。

右上の青クラスタは「高齢化進行・死亡率高」(秋田・島根・高知)、 左下の紫クラスタは「若年層多・死亡率低」(東京・神奈川・沖縄)と解釈可能な構造に分かれています。 「データに自然なグループ構造があれば、 k-means はそれを発見してくれる」ことが体感できる。

📐 数式

【k-means の目的関数:SSE 最小化】
$$\arg\min_{S_1, \ldots, S_k} \sum_{i=1}^{k} \sum_{x \in S_i} \|x - \mu_i\|^2$$
各クラスタ $S_i$ の中心 $\mu_i$ までの距離の二乗和を、 全クラスタで合計した量(SSE = Sum of Squared Errors)。 これを最小化する分割を探す

🔬 数式を「言葉」で読み解く

$k$
クラスタ数:事前に決める。 通常 2〜10。 エルボー法・シルエット係数で決定
$S_i$
$i$ 番目のクラスタ(サンプルの集合)
$\mu_i$
$S_i$ の中心(centroid):$\mu_i = \frac{1}{|S_i|} \sum_{x \in S_i} x$(クラスタ内サンプルの平均)
$\|x - \mu_i\|$
サンプル $x$ から中心 $\mu_i$ までのユークリッド距離
$\|x - \mu_i\|^2$
二乗することで「遠いほど強くペナルティ」

🧮 計算してみる

6都道府県のデータ(高齢化率と死亡率)で、 k=2 の k-means が実際にどう動くか、 反復計算を追ってみます。

STEP 0 データと初期化
都道府県高齢化率 x死亡率 y
秋田3919
高知3617
大阪2813
神奈川2611
東京239
沖縄2311

初期中心をランダムに:C1 = (40, 20)C2 = (20, 10)(k-means++ では確率的に選びますが、 簡単のためここでは固定)

STEP 1 イテレーション1:各点を最も近い中心に割り当て

各点から両中心までのユークリッド距離を計算:

d(C1)d(C2)割当
秋田 (39,19)√(1+1) = 1.4√(361+81) = 21.0C1
高知 (36,17)√(16+9) = 5.0√(256+49) = 17.5C1
大阪 (28,13)√(144+49) = 13.9√(64+9) = 8.5C2
神奈川 (26,11)√(196+81) = 16.6√(36+1) = 6.1C2
東京 (23,9)√(289+121) = 20.2√(9+1) = 3.2C2
沖縄 (23,11)√(289+81) = 19.2√(9+1) = 3.2C2

C1 クラスタ:秋田、高知(2点)/C2 クラスタ:大阪、神奈川、東京、沖縄(4点)

STEP 2 イテレーション1:中心を更新

各クラスタの平均を新しい中心とする:

  • 新 C1 = (平均(39,36), 平均(19,17)) = (37.5, 18.0)
  • 新 C2 = (平均(28,26,23,23), 平均(13,11,9,11)) = (25.0, 11.0)

中心が大きく動きました:C1 は (40,20) → (37.5, 18)、 C2 は (20,10) → (25, 11)

STEP 3 イテレーション2:再割り当て

新しい中心で再度距離計算:

d(新C1)d(新C2)割当
秋田 (39,19)1.8016.1C1(変化なし)
高知 (36,17)1.8012.5C1(変化なし)
大阪 (28,13)10.73.6C2(変化なし)
神奈川 (26,11)13.51.0C2(変化なし)
東京 (23,9)17.12.8C2(変化なし)
沖縄 (23,11)16.12.0C2(変化なし)

すべての点の割当が前回と同じ!中心も動かない。 収束完了。 たった2イテレーションで終了しました。

STEP 4 結果の解釈

最終結果

  • クラスタ1(高齢化進行群):秋田、 高知。 中心 (37.5, 18.0)。 「高齢化率約37.5%、 死亡率約18‰の県」
  • クラスタ2(中・低齢化群):大阪、 神奈川、 東京、 沖縄。 中心 (25.0, 11.0)。 「高齢化率約25%、 死亡率約11‰の県」

SSE(残差二乗和) = (秋田からC1の距離²) + (高知からC1の距離²) + ... ≈ 1.8² + 1.8² + 3.6² + 1.0² + 2.8² + 2.0² ≈ 34.5。 これより小さい分割は存在しません(この初期値からは)。

k=3 にしたら?大阪・神奈川を1群、 東京・沖縄を別群、 秋田・高知を1群、 のように細分化されます。 SSEはさらに小さく。 でもどこかで「k を増やしても効率が悪い」境界(=エルボー)が来ます。

🎓 k-means の設計判断 — k の決定・初期化・距離

1. k(クラスタ数)の決め方

「いくつのクラスタに分けるか」は分析者が決める。 客観的指標の選択肢:

2. 初期化:k-means++

ランダム初期化は局所最適に陥りやすい。 k-means++ は次の手順で賢く初期化:

  1. 1つ目の中心はランダム選択
  2. 2つ目以降は、 「既選択中心から遠い」サンプルほど高確率で選ぶ(距離の二乗に比例する確率)
  3. k 個の中心が分散して配置される

scikit-learn の KMeansデフォルトで k-means++。 さらに n_init 回試行して最良の SSE を採用。

3. 距離の選択

標準はユークリッド距離。 ただし他の選択肢もあります:

ただしユークリッド以外を使うと「平均」が幾何的に意味を失うので、 厳密には k-medoids(中心を実在サンプルに)が適切。

4. 計算量と収束性

計算量:$O(n \cdot k \cdot d \cdot \text{iter})$。 通常 iter は数十回。 n=100万 でも数秒で完了するのが強み。

収束性:k-means は必ず収束しますが、 大域最適解への収束は保証されません。 SSE は反復のたびに必ず減少(または同じ)となりますが、 局所最適に陥る可能性があります。 これが「複数回試行」が必要な理由。

NP困難性:k-means の大域最適解を求める問題は NP困難(k≥2 で)。 だから「最適解の近似」を反復で求めるしかない。

⚠️ よくある落とし穴

❌ k を恣意的に決める
「とりあえず k=3 でやりました」は通用しない。 必ずエルボー法シルエット係数で客観的に決定。 さらに事前理論(「3地域に分けたい」)と整合性を確認。 複数の k で結果を比較するのも頑健性チェックとして有効。 実例:n=47 都道府県なら、 k=2〜7 が現実的範囲。 k=10 など細かすぎると各クラスタが少数で意味を失う。
❌ 標準化なしで実行(致命的)
「人口」(億単位)と「失業率」(%)を標準化せずに距離を計算すると、 桁の大きい変数だけで距離が決まり、 無意味なクラスタになります。 必ず StandardScaler で平均0・分散1に揃えてから。

具体例:人口100万人の差は、 失業率5%の差より桁が6つ違う。 ユークリッド距離はほぼ「人口の差」になり、 失業率の情報がほぼ消える。
❌ 1回だけ実行して採用
初期化のランダム性に依存するので、 通常は異なる初期値で複数回実行し、 最良の SSE を持つ結果を採用。 scikit-learn の KMeans(n_init=10) は10回試行して最良を返す("auto" なら自動調整)。 学術論文では random_state を固定し再現可能にする。
❌ 非球形クラスタに無理やり
三日月型、 渦巻き型、 同心円のような複雑な形状は、 k-means では分割できません。 「球形クラスタ前提」の手法だから。 こうしたデータには DBSCAN(密度ベース)、 Spectral Clustering(グラフ理論)、 GMM(楕円体クラスタOK)などへ。

判定方法:PCA で2次元プロットして、 クラスタの形状を視覚的に確認。
❌ 外れ値の影響を無視
k-means の中心は平均なので、 1個の極端な値で中心が大きく動きます。 例えば「東京」が他から離れていると、 東京を含むクラスタの中心が東京寄りになり、 他県との関係が歪む。 対処:(i) 外れ値を別クラスタにする(k+1で実行)、 (ii) k-medians や k-medoids を使う、 (iii) 外れ値を事前除外。
❌ クラスタ結果を「客観的真実」と思う
k-means は「与えた指標と距離の枠組みで似ているもの」を集めるだけ。 違う指標で実行すれば違うクラスタが出ます。 結果は分析者の選択の反映。 「このデータをこう分類した」という形で報告すべき。 「真のクラスタ」が存在する前提自体が幻想のことも。
✅ クラスタの解釈をしっかり
「k=4 でクラスタリングしました」だけでは研究にならない。 各クラスタの変数の平均値・標準偏差・代表サンプルを確認し、 「クラスタ1は何の特徴を持つ群か」を解釈・命名する。 ここが分析者の腕の見せどころ。 例:クラスタA=「大都市」、 B=「地方中核」、 C=「地方過疎」のように。

📊 SSDSE実データでの応用

47都道府県の家計5項目データに k-means (k=3) を適用しました:

k-means結果

PC1 と PC2 の散布図上で、 3つのクラスタが綺麗に分離されているのが見えます。 各クラスタの特徴を解釈することで「家計タイプ」が見出せます。

🎯 k の選び方

クラスタ数判定

エルボー法とシルエット法で最適な k を探します。

📐 k-means の数学

損失関数

$$ J = \sum_{i=1}^{n} \sum_{j=1}^{k} z_{ij} \| x_i - \mu_j \|^2 $$

z_ij ∈ {0, 1}:i番目点がクラスタjに属するかどうか。 μ_j:クラスタjの中心。

アルゴリズム(Lloyd's algorithm)

  1. 初期中心をランダムに k個選ぶ
  2. 各点を最寄りの中心に割り当てる(assignment step)
  3. 各クラスタの中心を再計算(update step)
  4. 収束するまで 2-3 を繰り返す

計算量は O(n × k × d × iter)。 大規模データでは k-means++ や Mini-Batch k-means を使用。

k-means++ の初期化

「離れた点を初期中心に選ぶ」工夫。 通常のランダム初期化より速く・確実に良い解に収束。 sklearn のデフォルト。

⚠️ k-means の限界

これらの限界を超えるには、 GMM、 DBSCAN、 スペクトラルクラスタリングなどを検討。

🐍 Python での実装

① scikit-learn での基本

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #1。最初のスニペットです。SSDSE-B-2026 を読み込みます。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
 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 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}')
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:このステップは前処理/補助関数。本処理は次のスニペットに続く。

② 最適クラスタ数の探索

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #2。基本統計量を計算します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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')
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:数値が出力されたら、まず大きさ(オーダー)と符号を確認しよう。

③ デンドログラムの描画

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #3。可視化(散布図/樹形図/時系列プロット)を描きます。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
4
5
6
from scipy.cluster.hierarchy import linkage, dendrogram

Z = linkage(X_std, method='ward')
plt.figure(figsize=(14, 6))
dendrogram(Z, labels=labels, leaf_rotation=90)
plt.show()
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:プロットの形状から定性的な傾向(単調性・周期性)を読み取る。

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

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

📍 体系階層のパス

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

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

中心の概念から放射状に、 前提・兄弟・発展形・応用先などの関係性を矢印で結びます。 横の繋がりを見るのに最適。 ノードをドラッグ、 ホイールでズーム、 クリックで遷移

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

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

大きな円が小さな円を包含する Circle Packing 図。 「k-means法」は緑色でハイライト

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

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

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

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

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

💡 ジャストインタイム学習のヒント:3つの視点を行き来することで、 概念を多角的に理解できます。 包含マップやツリーマップはズーム/ドリルダウンで大分類から細部まで探索できます。

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

直感 計算 🎓 設計判断 🧮 SSDSE-B 実例 🐍 実装バリエーション ⚠️ 落とし穴集 kの決定 DBSCAN代替 関連用語マップ

🧮 SSDSE-B を使った k-means の実例 — 47都道府県のクラスタリング

SSDSE-B 2020年の主要4指標で 47都道府県を k=4 にクラスタリングし、 地域類型化を試みます。

① データ準備と標準化

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #4。主要な指標(係数・統計量・スコア)を算出します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

df = pd.read_csv('data/raw/SSDSE-B-2023.csv', encoding='shift_jis', header=[0,1])
df.columns = ['_'.join(c).strip() for c in df.columns]
d = df[df['年度_Year'] == 2020].dropna()
features = ['高齢化率', '1人当たり県民所得', '人口密度', '合計特殊出生率']
X = StandardScaler().fit_transform(d[features])
print(f'標準化済み X.shape = {X.shape}')
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:算出された統計量を判定基準と比較し、有意性/効果量を評価する。

② エルボー法で k を決定

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #5。仮説検定・モデル評価を行います。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import matplotlib.pyplot as plt
sse = []
for k in range(1, 11):
    km = KMeans(n_clusters=k, n_init=10, random_state=42).fit(X)
    sse.append(km.inertia_)
plt.plot(range(1, 11), sse, marker='o')
plt.xlabel('k'); plt.ylabel('SSE')
plt.axvline(4, color='r', linestyle='--', label='選定 k=4')
plt.show()
# 典型的にエルボーが k=3 or 4 にある
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:p 値や信頼区間と合わせて読み、効果の有無+大きさを両輪で判断する。

③ シルエット係数で k を裏付け

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #6。結果を整形して表示します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
4
5
6
7
from sklearn.metrics import silhouette_score
for k in range(2, 8):
    km = KMeans(n_clusters=k, n_init=10, random_state=42).fit(X)
    sil = silhouette_score(X, km.labels_)
    print(f'k={k}: silhouette = {sil:.3f}')
# k=2: 0.32, k=3: 0.30, k=4: 0.28, k=5: 0.24
# シルエット最大の k=2 だが、 解釈可能性で k=4 を選ぶこともある
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:表示された数値テーブルから個別の都道府県の位置づけを読み取る。

④ k=4 でクラスタリング・解釈

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #7。47都道府県データに当てはめて確認します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
4
5
6
7
8
9
km = KMeans(n_clusters=4, n_init=10, random_state=42).fit(X)
d['cluster'] = km.labels_
# 各クラスタの特徴を平均値で要約
profile = d.groupby('cluster')[features].mean().round(2)
print(profile)
# 各クラスタに属する都道府県
for c in range(4):
    members = d[d['cluster']==c]['都道府県_Prefecture'].tolist()
    print(f'クラスタ {c}: {members}')
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:SSDSE-B-2026 の実値に当てはめると教科書例より分散が大きいことに注意。

典型結果:

  • クラスタ0「大都市」:東京・神奈川・大阪・愛知(人口密度・所得高、 出生率低、 高齢化中)
  • クラスタ1「地方中核」:宮城・福岡・京都・広島など(中程度の指標群)
  • クラスタ2「地方過疎」:秋田・島根・高知(高齢化・人口減)
  • クラスタ3「西日本郊外」:徳島・佐賀・宮崎(所得低・出生率高)

🐍 実装バリエーション — scikit-learn / scipy / faiss

(A) scikit-learn KMeans — 標準

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #8。比較・別パターンを検討します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
4
5
6
from sklearn.cluster import KMeans
km = KMeans(n_clusters=4, init='k-means++', n_init='auto', max_iter=300,
            random_state=42).fit(X)
print(km.labels_)        # 各サンプルのクラスタ番号
print(km.cluster_centers_)  # 中心
print(km.inertia_)        # SSE
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:別パターンと比べることで、手法選択の感度を体感できる。

(B) scikit-learn MiniBatchKMeans — 大規模データ向け

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #9。ハイパーパラメータを変えて再計算します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
from sklearn.cluster import MiniBatchKMeans
mbk = MiniBatchKMeans(n_clusters=4, batch_size=100, n_init=10, random_state=42)
mbk.fit(X_large)  # n=100万 でも数秒
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:ハイパーパラメータで結果が大きく変わる場合は安定性を疑う。

(C) scipy.cluster.vq — 低レベル

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #10。最終結果のまとめ・保存を行います。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
4
from scipy.cluster.vq import kmeans, vq
centroids, distortion = kmeans(X, 4)
clusters, _ = vq(X, centroids)
# シンプルだが n_init などのオプションが少ない
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:最終結果は CSV/プロットとして保存しておくと後続分析で再利用できる。

(D) k-medoids (PAM) — ロバスト代替

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #11。可視化を仕上げ、レポートに統合します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
from sklearn_extra.cluster import KMedoids
kmed = KMedoids(n_clusters=4, metric='manhattan', random_state=42).fit(X)
# 中心が「実在サンプル」になる。 外れ値に強い
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:レポート用には数値だけでなく可視化と注釈をセットで提示する。

(E) Gaussian Mixture — 確率的拡張

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #12。追加検証・感度分析を実行します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
4
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=4, covariance_type='full', random_state=42).fit(X)
proba = gmm.predict_proba(X)  # 各クラスタへの所属確率
# 楕円体クラスタOK、 ソフトクラスタリング
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:感度分析の結果が安定していれば、結論の信頼性が高まる。

(F) FAISS — 数億規模の超高速

🎯 このコードでやること:k-means — 重心型分割クラスタリングに関連するステップ #13。応用パターン(別データ・別手法)に拡張します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) # 47都道府県を k=4 で k-means 分割(標準化済み特徴を使用) # X (47行, 3列): # pref z_age z_inc z_pop # 0 北海道 0.31 -0.42 0.85 # 1 青森県 1.45 -1.21 -0.62 # 2 岩手県 1.39 -0.72 -0.68
1
2
3
4
5
import faiss
kmeans_faiss = faiss.Kmeans(d=X.shape[1], k=4, niter=20, verbose=False)
kmeans_faiss.train(X.astype('float32'))
_, labels = kmeans_faiss.index.search(X.astype('float32'), 1)
# GPU 対応、 大規模ベクトル検索(埋め込み)に必須
📤 実行例(実行時の標準出力) クラスタラベル (47件): [2, 0, 0, 3, 0, 0, ...] Inertia (SSE) = 64.21, シルエット係数 = 0.412 重心: 0=地方高齢, 1=都市部, 2=北海道型, 3=中核地方 処理完了
💬 読み方:応用パターンの結果を比較すると、手法の適用範囲が見える。

⚠️ 追加の落とし穴 — k-means の実務(既存に追加)

❌ 高次元データに素朴に適用
次元数 d > 50-100 になると次元の呪いで距離の差が縮まり、 クラスタ構造が見えなくなる。 対策:(i) 事前に PCA で次元削減(10-20次元へ)、 (ii) UMAP・t-SNE で2-3次元化(可視化用)、 (iii) ドメイン知識で重要変数のみ選択、 (iv) スパースな高次元なら別アプローチ(NMF 等)。 SSDSE-B でも全100変数を入れるのは推奨せず、 6-12 変数に絞るのが現実的。
❌ 「クラスタ数 = 自然な真の数」と思い込む
エルボー法・シルエットで「最適 k」を求めても、 それは数学的最適にすぎない。 データに真のクラスタ構造があるとは限らない。 連続的に分布するデータでも k-means は強制的に k 個に分割する。 ギャップ統計量で「クラスタ構造の有無」を事前検定するか、 結果を別の k で複数試して頑健性を確認する。 「分割結果」と「自然な群」を混同しない。
❌ クラスタサイズの不均衡を無視
k-means は「球形クラスタかつ等サイズ」を暗黙に仮定する目的関数を最小化する。 実データではクラスタが大小さまざまで、 小クラスタが大クラスタに吸収されたり、 大クラスタが不自然に分割されることがある。 サイズ不均衡を考慮するなら、 GMM、 重み付き k-means、 階層クラスタリング、 DBSCAN(密度ベース)などを検討。
❌ クラスタラベルを「順序」と扱う
k-means の出力ラベル(0, 1, 2, ...)は名義尺度であり、 順序や大小に意味はない。 「クラスタ0より2が良い」のような比較は無意味。 また実行ごとにラベル番号が変わるため、 報告時にはクラスタ特性で命名(「都市型」「過疎型」など)するのが標準。 ラベル番号での集計や可視化は誤解を生む。
❌ 新サンプルの分類を考慮しない
k-means は訓練時に決まった中心に新サンプルを割り当てる(最近隣中心)。 これを「分類モデル」と思い込むのは要注意:新サンプルが既存どのクラスタにもフィットしない場合、 強制的に最寄りクラスタに入れられる。 異常検知が必要な場面では、 距離の閾値超過を「クラスタ外」として扱う実装が必要。
❌ カテゴリ変数を素のまま入れる
k-means はユークリッド距離ベースなので、 カテゴリ変数(性別・地域・色など)には適用できない。 one-hot エンコード後でも、 高次元でスパースになり距離が無意味化する。 カテゴリ混在データには k-modes(カテゴリ用)、 k-prototypes(混在用)を使う。 距離尺度に Gower 距離を採用した階層クラスタリングも代替候補。
❌ 結果の再現性を担保しない
k-means の初期化はランダムなので、 random_state を固定しないと毎回違う結果になる。 学術論文・本番運用では必ず random_state=42 等を明示。 また n_init(複数初期化試行回数)の指定も再現性に影響する。 さらに、 ハイパーパラメータ・前処理 (標準化方法) も全て記録し、 完全な再現性確保が責任。

🧮 SSDSE-B-2026 で k-means を即実行

最新の SSDSE-B-2026 を使い、 47 都道府県を 4 クラスタに分けるコード。 教材標準パスを直書きしています。

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', header=[0,1])
X = df[['総人口', '出生率', '高齢化率', '大学進学率']].dropna()
X_scaled = StandardScaler().fit_transform(X)

km = KMeans(n_clusters=4, n_init=10, random_state=42).fit(X_scaled)
print(km.labels_)        # 各都道府県のクラスタ番号
print(km.inertia_)       # SSE(小さいほど締まりが良い)

出力例: 東京・大阪は「人口規模グループ」、 秋田・高知は「高齢化グループ」、 沖縄は単独クラスタ寄りなど、 SSDSE-B-2026 の最新値で確かめられます。