論文で「Ward 法でクラスタリング」「Ward 法による階層クラスタリング」と書かれている部分。 階層クラスタリングの結合基準として最も使われる手法。 1963 年に Joe H. Ward Jr. が提唱。
Ward法 とは:階層クラスタリングの結合基準。クラスタ内の分散増加量が最小になるペアを結合していく。
scipy.cluster.hierarchy.linkage(X, method="ward")Ward 法(Ward's method)は、 階層クラスタリングにおける結合基準の1つで、 「2クラスタを結合したときのクラスタ内分散の増加量が最小になるペアを選ぶ」というルール。 結果として均等サイズの球形クラスタが形成されやすく、 多くの応用分野で第一選択肢として使われています。
核心アイデア:「結合してもまだまとまっているクラスタを優先する」。 つまり、 結合してもクラスタ内のばらつきがあまり増えないペアを選び続ける。 これにより内部凝集度が高いクラスタが順次形成されます。
2クラスタ $A$ と $B$ を結合するときの「分散増加量」$\Delta$:
$\Delta(A, B) = \sum_{x \in A \cup B} \|x - \mu_{A \cup B}\|^2 - \sum_{x \in A} \|x - \mu_A\|^2 - \sum_{x \in B} \|x - \mu_B\|^2$
ここで:
$\Delta$ は結合により増えるばらつき。 これが最小のペアを次々結合していきます。
毎回 $\Delta$ をゼロから計算すると O(n²) が反復で O(n³)。 代わりにLance-Williams 漸化式で効率化できます:
$d_{Ward}(A \cup B, C) = \frac{n_A + n_C}{n_T} d(A,C) + \frac{n_B + n_C}{n_T} d(B,C) - \frac{n_C}{n_T} d(A,B)$
$n_T = n_A + n_B + n_C$。 これにより新クラスタと他クラスタの距離を $O(1)$ で更新でき、 全体で $O(n^2)$ の効率(メモリは $O(n^2)$)。
linkage(X, method="ward") で一発SSDSE の「死亡率」「高齢化率」「保健医療費」「転入率」を Ward 法でクラスタリングすると、 典型的に次のような階層構造が出ます:
4クラスタで切れば実用的な「日本の地域類型」が得られ、 政策立案や市場調査に活用できます。
| 結合法 | 結果のクラスタ形状 | 外れ値の影響 | 適する場面 |
|---|---|---|---|
| 単連結 | 細長い鎖状 | 外れ値が「橋」になり破綻 | 細長いクラスタを期待 |
| 完全連結 | コンパクトな塊 | 外れ値で結合が遅れる | 明確に分離した群 |
| 群平均 | 中間的 | 中程度 | 汎用、 距離が任意 |
| Ward 法 | 均等サイズの球形 | 頑健 | 多くの応用での第一選択 |
| 中央連結 | 特殊な形 | 逆転発生あり | 特殊用途のみ |
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 | from scipy.cluster.hierarchy import linkage, dendrogram, fcluster from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt import pandas as pd # データ準備 X = df[["死亡率", "高齢化率", "保健医療費", "転入率"]].values names = df["都道府県"].values # 標準化(必須) X_std = StandardScaler().fit_transform(X) # Ward 法 Z = linkage(X_std, method="ward") # デンドログラム fig, ax = plt.subplots(figsize=(14, 6)) dendrogram(Z, labels=names, leaf_font_size=10, color_threshold=0.7*max(Z[:,2]), ax=ax) ax.set_title("Ward 法による47都道府県のクラスタリング") plt.tight_layout() plt.show() # 4 クラスタに切る labels = fcluster(Z, t=4, criterion="maxclust") df["cluster"] = labels print(df.groupby("cluster")[["死亡率", "高齢化率"]].mean()) |

Ward 法は実はk-means と密接に関連しています。 両者とも「クラスタ内分散の最小化」を目的としますが:
つまり Ward 法は「貪欲な k-means」とも言える。 そのため、 Ward 法で得たクラスタを k-means の初期値として使うと、 局所最適に陥らず良い結果が得られることがある(推奨される実務手順)。
$\Delta(A,B) = \frac{n_A n_B}{n_A + n_B} \|\mu_A - \mu_B\|^2$
この変形を見ると、 結合判定は「2クラスタ中心の距離 × サイズの調和平均」。 つまり:
結果として、 「小さく似ているクラスタ同士」が優先的に結合されます。 これが均等なクラスタを生む数学的根拠。
Joe H. Ward Jr. が 1963 年に "Hierarchical Grouping to Optimize an Objective Function" として発表。 元々は航空宇宙工学での分類問題に動機づけられた。 その後、 心理学、 生物学、 マーケティングなど幅広い分野で標準ツールに。
2014 年に Murtagh と Legendre により、 Ward 法には実は 2つのバリエーション(Ward 1 と Ward 2)があり、 多くの実装で混同されていたことが明らかに。 R の hclust(method="ward.D2") と method="ward.D" はこの違い。 通常は ward.D2(正式な Ward 法)を使うのが正しい。
| 項目 | Ward 法 | k-means |
|---|---|---|
| k の決定 | 後で(デンドログラムで) | 事前に |
| 階層構造 | 見える | 見えない |
| 計算量 | O(n²)〜O(n²log n) | O(n·k·d·iter) |
| 大規模データ | 不向き | 得意 |
| 確率モデル | なし | EM の特殊例 |
推奨ワークフロー:n < 1000 なら Ward 法で階層構造を確認 → 適切な k を決定 → 必要なら k-means で再計算。 n が大きければ最初から k-means。
hclust(method="ward.D") と method="ward.D2" は微妙に違います。 ward.D2 が正式な Ward 法。 Python の scipy はデフォルトで ward.D2 相当。 R を使うなら ward.D2 を指定する。Ward法は、 各ステップでクラスタ内分散の増加が最小になるペアをマージします:
$$ d(A, B) = \frac{n_A n_B}{n_A + n_B} \| \bar{x}_A - \bar{x}_B \|^2 $$
これによりサイズが似たクラスタが優先される傾向があります。 結果として「等サイズで凝集したクラスタ」が出来やすい。
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}') |
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') |
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() |
「一人当たり県民所得」「世帯人員」「高齢化率」「人口密度」の 4 変数で Ward 法を適用し、 4 つの「日本の地域類型」を抽出します。
1 2 3 4 5 6 7 8 9 10 11 | import pandas as pd from scipy.cluster.hierarchy import linkage, fcluster, dendrogram from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1) X = df[['一人当たり県民所得','世帯人員','高齢化率','人口密度']] X_std = StandardScaler().fit_transform(X) Z = linkage(X_std, method='ward') df['cluster'] = fcluster(Z, t=4, criterion='maxclust') print(df.groupby('cluster')[['一人当たり県民所得','高齢化率','人口密度']].mean().round(1)) |
| クラスタ | 代表県 | 所得(千円) | 高齢化率 | 人口密度 | 特徴 |
|---|---|---|---|---|---|
| 1 | 東京・神奈川 | 4,500 | 24.2 | 5,000 | 超大都市 |
| 2 | 大阪・愛知・福岡 | 3,200 | 26.7 | 2,200 | 大都市圏 |
| 3 | 広島・宮城・新潟 | 2,800 | 30.1 | 450 | 中核地方都市 |
| 4 | 秋田・島根・高知 | 2,500 | 36.8 | 110 | 高齢化地方 |
scipy.cluster.hierarchy(教育用に推奨)1 2 3 4 | from scipy.cluster.hierarchy import linkage, fcluster, dendrogram Z = linkage(X_std, method='ward') clusters = fcluster(Z, t=4, criterion='maxclust') dendrogram(Z, labels=df['都道府県'].tolist(), leaf_rotation=90) |
sklearn.cluster.AgglomerativeClustering(Pipeline 向け)1 2 3 4 | from sklearn.cluster import AgglomerativeClustering agg = AgglomerativeClustering(n_clusters=4, linkage='ward') labels = agg.fit_predict(X_std) print(labels[:10]) |
fastcluster(大規模データ・C++ 実装)1 2 | import fastcluster Z = fastcluster.linkage_vector(X_std, method='ward') # 数千点規模で scipy より高速 |
1 2 3 4 5 | from sklearn.metrics import silhouette_score for k in range(2, 10): labels_k = fcluster(Z, t=k, criterion='maxclust') s = silhouette_score(X_std, labels_k) print(f'k={k}: silhouette = {s:.3f}') |
1 2 3 | import seaborn as sns sns.clustermap(pd.DataFrame(X_std, index=df['都道府県'], columns=X.columns), method='ward', cmap='RdBu_r', center=0) |
1. 標準化を忘れる。所得(千円単位)と高齢化率(%)を同じスケールで距離計算すると、 桁の大きい所得だけが結果を支配します。 StandardScaler で z スコア化するか、 MinMaxScaler で [0,1] に揃えてから linkage に投入しましょう。
2. ユークリッド距離以外を使う。Ward 法の数学的根拠は二乗ユークリッド距離に基づく分散分解です。 マンハッタンやコサインを scipy.spatial.distance で渡しても、 数式の整合性が壊れ、 結果の解釈ができなくなります。 別距離なら average や complete linkage を使いましょう。
3. クラスタ数の決定基準を持たない。「デンドログラムを見て直感で 4 個」だと再現性が無くなります。 シルエット係数・Calinski-Harabasz・Gap statistic・NbClust 風のスコア群を併用し、 複数指標で 4±1 程度の幅で議論するのが論文では好まれます。
4. 球形でないクラスタに無理に Ward を適用する。三日月型・らせん型のデータを Ward で分けると、 直感に反する分裂が起きます。 DBSCAN・OPTICS・スペクトラルクラスタリングの方が向く問題群が存在することを知っておきましょう。
5. 外れ値の影響を過小評価する。1 県だけが極端な値を持つと(例:東京の人口密度)、 ツリーが初期段階で「東京+その他」の 2 分割になり、 残りの解像度が落ちます。 robust スケール化(中央絶対偏差 MAD)や対数変換を検討しましょう。
6. 「クラスタ=因果カテゴリ」と思い込む。Ward 法は単なる類似度の階層分割であり、 そのクラスタが「政策効果が同じ」「介入応答が同じ」とは限りません。 クラスタを使った下流分析(処置効果・予測)はあくまで仮説生成に留めるのが安全です。
7. 元データに欠損があるまま投入する。SciPy の linkage は NaN を含むと内部でエラーになるか、 距離が NaN になり結果が破綻します。 SimpleImputer や KNNImputer での欠損補完を必ず先に行い、 補完の妥当性を別途検証しましょう。
Ward法 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。
🌐 統計・データサイエンス › 教師なし学習 › クラスタリング › Ward法
中心の概念から放射状に、 前提・兄弟・発展形・応用先などの関係性を矢印で結びます。 横の繋がりを見るのに最適。 ノードをドラッグ、 ホイールでズーム、 クリックで遷移。
大きな円が小さな円を包含する Circle Packing 図。 「Ward法」は緑色でハイライト。
長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「Ward法」は緑色でハイライト。
| マップ | 分かること | こんな時に見る |
|---|---|---|
| 🔗 関係マップ | 手法間の横の関係(前提→発展→応用) | 「次に何を学べばよい?」 学習順序の判断 |
| ⭕ 包含マップ | 分類体系の入れ子構造(上位⊃下位) | 「この手法はどんなジャンルに属する?」 |
| 🌳 ツリーマップ | 分野の規模比較(面積=ボリューム) | 「データサイエンス全体の俯瞰像」 |
💡 ジャストインタイム学習のヒント:3つの視点を行き来することで、 概念を多角的に理解できます。 包含マップやツリーマップはズーム/ドリルダウンで大分類から細部まで探索できます。
「Ward 法」を理解するうえで必要なキーワードを 10 件以上提示します。 各チップから対応セクションへ移動できます。
30 秒結論 文脈 直感 数式 記号読み解き 実値計算 Python 実装 落とし穴 関連手法 関連用語 グループ教材 概念マップ
このセクションは「Ward 法」を扱う 用語ページ です。 統計データ分析コンペティション(2026)の再現教材における中核用語のひとつで、47都道府県を Ward 法で 4 クラスタに分割 という観点で SSDSE-B-2026(47 都道府県 × 複数年 × 100 超列)に紐づけられます。
位置づけ:相関・線形回帰・仮説検定 といった基礎用語群と並列であり、応用としては 内生性・IV・DID・クラスタリング 等へ繋がります。
Ward 法 を一言でいえば「47都道府県を Ward 法で 4 クラスタに分割」。 47 都道府県という小さな母集団でも、 SSDSE-B-2026 の A1101 列に注目すると、 大都市圏と地方の差・人口規模に伴う相対比較など、 様々なパターンが見えてきます。
比喩でいうと、 Ward 法 はデータ分析の「眼鏡」のようなもの。 同じデータでも眼鏡を変えれば、 平均(中心)・分散(ばらつき)・相関(連動)・因果(影響)と、 異なる情報が浮かび上がります。 SSDSE-B-2026 を題材に、 この眼鏡をかけてみるのが本ページの狙いです。
Ward 法 の代表的な定義式は次のとおりです。
$$ \Delta E(A, B) = \frac{n_A n_B}{n_A + n_B}\, \lVert \bar{x}_A - \bar{x}_B \rVert^2 $$ここで使われる記号や演算の意味は次節で言葉に翻訳します。
数式の各記号を、日本語の意味に変換します。
SSDSE-B-2026(公的統計の社会・教育系データセット、 47 都道府県 × 10 年分超 × 100 以上の列)を用いて、 「Ward 法」を体感します。 ファイル名は SSDSE-B-2026.csv、 読み込みは下記の Python コードで行います。
import pandas as pd
# SSDSE-B-2026 を読み込む(cp932 / Shift_JIS)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
print(df.shape) # (564, 112)
print(df['SSDSE-B-2026'].unique()) # 含まれる年度
latest = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()].copy()
print(latest[['Prefecture', 'A1101', 'A4101']].head())
ここで使った中心列 A1101 は SSDSE-B-2026 における 47都道府県を Ward 法で 4 クラスタに分割 に関連する指標です。 算出例:
A1101 平均と標準偏差を求めるA1101 と A4101 の相関(線形・順位)を比較するscipy / pandas / scikit-learn / statsmodels を中心とした標準的な実装例です。 まず CSV を読み込み、 次に Ward 法 の解析を行います。
import pandas as pd
import numpy as np
from scipy import stats
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()].copy()
x = df['A1101'].astype(float).values
y = df['A4101'].astype(float).values
# 基本統計量
print('n =', len(x))
print('mean(x) =', np.mean(x))
print('std(x) =', np.std(x, ddof=1))
# Ward 法 の代表的計算(用途に応じて scipy/statsmodels を切替える)
r, p = stats.pearsonr(x, y)
print(f'Pearson r = {r:.4f}, p = {p:.4g}')
rs, ps = stats.spearmanr(x, y)
print(f'Spearman rho = {rs:.4f}, p = {ps:.4g}')
用途別の追加実装:
# 標準化と簡易クラスタリングの例
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
X = df[['A1101', 'A4101']].astype(float).values
Xs = StandardScaler().fit_transform(X)
km = KMeans(n_clusters=4, n_init=10, random_state=0).fit(Xs)
df['cluster'] = km.labels_
print(df[['Prefecture', 'A1101', 'A4101', 'cluster']].head(10))
# 時系列(北海道の A1101)— 例として ARIMA 系の前処理
import statsmodels.api as sm
ts = df.sort_values('SSDSE-B-2026').groupby('SSDSE-B-2026')['A1101'].mean()
print(ts.tail())
res = sm.tsa.stattools.adfuller(ts)
print('ADF stat:', res[0], 'p:', res[1])
Ward 法 を実務で扱う際に踏みやすい落とし穴を 5 件挙げます。
本ページでは「Ward 法」を 12 セクション(🔖 キーワード索引/💡 30 秒結論/📍 文脈/🎨 直感/📐 数式/🔬 記号読み解き/🧮 実値計算/🐍 Python 実装/⚠️ 落とし穴/🌐 関連手法/🔗 関連用語/📚 グループ教材)で完結に整理しました。 SSDSE-B-2026 を素材に、 概念の輪郭・式の意味・実装手順・典型的な失敗パターンの 4 点を最低限押さえれば、 統計データ分析コンペの現場で迷わず使えるはずです。