論文で階層クラスタリングの結果として登場する樹形図。 「デンドログラム」「樹形図」「ward 法のデンドログラムを示す」のような形で。 47都道府県のような中規模データの階層構造を可視化する最重要ツール。
デンドログラム とは:階層クラスタリングの結果を樹形図で表現したもの。縦軸は結合距離、横軸は個体。
scipy.cluster.hierarchy.dendrogram(Z)デンドログラム(dendrogram, 樹形図)は、 階層クラスタリングの結果を樹形図の形で可視化したもの。 「どのサンプル同士が、 どの距離で結合するか」が一目で分かる、 階層クラスタリングの最終成果物です。
語源はギリシャ語 "dendron"(木)+ "gramma"(描いたもの)。 まさに「木の絵」。 生物学の系統樹(phylogenetic tree)も同じ概念で、 進化的近縁関係を樹形図で表します。
デンドログラムは3要素から成ります:
底から上に向かって、 「最初は各サンプルが独立」→「順次結合」→「最後に全部1つに」という過程を表現。
低い位置で結合する2サンプル = 似ている。 高い位置で結合 = 大きく異なる。
デンドログラムの真の威力は「後からクラスタ数を決められる」こと。 任意の高さで横に切ると、 そこから下のサブツリーが「k 個のクラスタ」になります:
切断高さの決め方:
scipy の dendrogram() 関数では、 color_threshold パラメータで「ここから上は色分けしない」と指定すると、 自然なクラスタが色で区別されます:
デフォルトは「最大距離の 70%」。 これにより視覚的にクラスタ構造が際立ちます。
SSDSE データを Ward 法でクラスタリングした典型的なデンドログラム解釈:
この階層構造から、 分析目的に応じた k を選べます。 細かく分析するなら k=8 で各地域圏に、 大きな区分なら k=2 で大都市 vs 地方に。
デンドログラムは、 単にクラスタを示すだけでなく、 多くの情報を提供します:
失敗1:横軸の順序を意味あるものと思う
デンドログラムの横軸(葉の順序)は、 アルゴリズムの実装や枝の入れ替えで変わります。 「左から3番目だから」「真ん中だから」に意味はない。 重要なのは結合の高さと階層構造のみ。
失敗2:「最も似ている」を誤読
「2サンプルが隣同士に描かれている = 似ている」は誤り。 隣でも結合高さが高ければ似ていない。 必ず結合高さで判断。
失敗3:切断高さの恣意性
「だいたいここで切ろう」は主観的。 客観的な指標と組み合わせて決定すべき。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from scipy.cluster.hierarchy import linkage, dendrogram from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt # 標準化 X_std = StandardScaler().fit_transform(X) # 階層クラスタリング 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 法によるデンドログラム", fontsize=14) ax.set_ylabel("結合距離") plt.tight_layout() plt.show() |

デンドログラムが意味を持つには、 結合距離が「ウルトラメトリック」(ultrametric)という強い条件を満たす必要があります:
$d(x, z) \le \max(d(x, y), d(y, z))$
これは通常の距離の3角不等式 $d(x,z) \le d(x,y) + d(y,z)$ より強い条件。 これがあるおかげで、 樹形図として一意に表現できます。
結合基準によってはウルトラメトリックが破れ(中央連結など)、 樹形図に「逆転」(枝が交差)が起きます。 Ward 法、 単連結、 完全連結、 群平均では発生しません。
デンドログラムには $2^{n-1}$ 通り の表示方法があります(各内部節点で左右の子を入れ替えられる)。 例えば 47県のデンドログラムは $2^{46} \approx 7 \times 10^{13}$ 通りの表示が可能。 でも階層構造(どこで結合するか)は同じ。
scipy では各実装が独自の順序で表示します。 多くの場合「リーフ間の距離を最小化する」順序(optimal leaf ordering)が使われ、 視覚的に分かりやすい配置になります。
デンドログラムから「2サンプル $x_i, x_j$ が結合した距離」を取り出した行列をcophenetic 距離行列と呼びます。
cophenetic correlation = (元の距離行列)vs(cophenetic 距離行列)の Pearson 相関。 これが大きい(0.7以上)と、 デンドログラムが元の距離構造を忠実に表現していることを意味します。 0.5以下なら、 階層クラスタリング自体が不適切な可能性。
生物学の系統樹(phylogenetic tree)もデンドログラムの一種。 種間の DNA 距離から進化的近縁関係を表現します。 ただし、 系統樹は「時間軸」(進化年代)を含むことが多く、 縦軸の意味が違います。
クラスタリングのデンドログラム:縦軸 = 不類似度
系統樹:縦軸 = 進化時間 or 遺伝距離
n が大きいと(n > 100)、 デンドログラムの葉が密集して読めなくなります。 対策:
家計5項目で Ward 法を実行した結果。 「色分けされたクラスタ」は事前に指定した閾値で切ったもの。 距離が大きいところで切れば、 数の少ない大きなクラスタに分かれます。
seaborn の clustermap は、 行・列両方をクラスタリングしてヒートマップに重ねる強力なツール。
系統樹のような円形表示。 多数の葉でも見やすい。
生物分類でよく使われる、 デンドログラムに時系列情報を加えたもの。
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() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 基本パターン import pandas as pd import numpy as np from scipy import stats import matplotlib.pyplot as plt import seaborn as sns # データ読み込み df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932') # 基本統計量 df.describe() # 可視化 sns.pairplot(df[['食料費', '教育費', '住居費']]) plt.show() |
このページの上にある3つの概念マップ(関係マップ、 包含マップ、 ツリーマップ)でこの概念の位置づけが視覚的に分かります。 関連手法を辿って学習を進めましょう。
統計データ活用コンペティションのSSDSE-B-2026データは、 47都道府県の社会経済データ。 この概念を使って以下のような分析ができます:
| 機能 | Python (pandas) | Python (scipy) |
|---|---|---|
| 要約統計 | df.describe() | stats.describe() |
| 平均 | df.mean() | np.mean() |
| 標準偏差 | df.std() | np.std() |
| 相関 | df.corr() | stats.pearsonr() |
| t検定 | — | stats.ttest_ind() |
| 回帰 | — | stats.linregress() |
| 分布フィッティング | — | stats.norm.fit() |
この概念は、 他の多くの統計概念と密接に関連しています。 ジャストインタイム型学習では、 必要に応じて関連用語へジャンプしながら全体像を構築します。
| グループ | 主要概念 |
|---|---|
| 記述統計 | 平均、 中央値、 最頻値、 分散、 標準偏差、 共分散、 相関係数 |
| 可視化 | ヒストグラム、 散布図、 箱ひげ図、 ヒートマップ |
| 推測統計 | 標本平均、 標準誤差、 信頼区間、 p値、 有意水準 |
| 確率分布 | 正規分布、 t分布、 χ²分布、 F分布、 二項分布 |
| 仮説検定 | t検定、 F検定、 χ²検定、 ノンパラ検定 |
| 回帰 | 単回帰、 重回帰、 OLS、 Ridge、 LASSO |
| 分類 | ロジスティック回帰、 決定木、 SVM、 k-NN |
| 教師なし学習 | クラスタリング、 PCA、 因子分析 |
| 時系列 | ARIMA、 VAR、 指数平滑法、 自己相関 |
| 因果推論 | DiD、 IV、 傾向スコア、 交絡変数 |
| 前処理 | 標準化、 正規化、 欠損値処理、 多重共線性対策 |
| 評価 | R²、 残差、 CV、 RMSE、 効果量 |
デンドログラム がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。
🌐 統計・データサイエンス › 教師なし学習 › クラスタリング › デンドログラム
中心の概念から放射状に、 前提・兄弟・発展形・応用先などの関係性を矢印で結びます。 横の繋がりを見るのに最適。 ノードをドラッグ、 ホイールでズーム、 クリックで遷移。
大きな円が小さな円を包含する Circle Packing 図。 「デンドログラム」は緑色でハイライト。
長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「デンドログラム」は緑色でハイライト。
| マップ | 分かること | こんな時に見る |
|---|---|---|
| 🔗 関係マップ | 手法間の横の関係(前提→発展→応用) | 「次に何を学べばよい?」 学習順序の判断 |
| ⭕ 包含マップ | 分類体系の入れ子構造(上位⊃下位) | 「この手法はどんなジャンルに属する?」 |
| 🌳 ツリーマップ | 分野の規模比較(面積=ボリューム) | 「データサイエンス全体の俯瞰像」 |
💡 ジャストインタイム学習のヒント:3つの視点を行き来することで、 概念を多角的に理解できます。 包含マップやツリーマップはズーム/ドリルダウンで大分類から細部まで探索できます。
デンドログラム(dendrogram、 樹形図)を確実に使いこなすための関連キーワードを難易度別に整理しました。
合成データではなく、 47 都道府県の家計支出(食料費、 教育費、 交通費、 通信費 4 変数)でデンドログラムを作成する手順を、 数値例で示します。
SSDSE-B-2026 から 4 変数を抽出します。 単位の異なる変数を扱うので、 標準化(z スコア化)が必須です。
1 2 3 4 5 6 7 8 9 10 11 | # 4 変数の平均と標準偏差(年間支出、 単位:千円、 仮想値) 食料費 :平均 850、 標準偏差 80 教育費 :平均 120、 標準偏差 35 交通費 :平均 290、 標準偏差 45 通信費 :平均 140、 標準偏差 18 # z スコア = (x - 平均) / 標準偏差 # 例:東京の食料費 1080 千円 → z = (1080-850)/80 ≈ +2.88 # 例:東京の教育費 220 千円 → z = (220-120)/35 ≈ +2.86 → 東京は 4 変数すべてで上位の特異値、 個別クラスタになる傾向 |
標準化後、 都道府県ペアごとのユークリッド距離 d(A, B) = √Σ(z_Aᵢ − z_Bᵢ)² を計算します。 47 都道府県なら 47×46/2 = 1081 ペア。
1 2 3 4 5 | # 距離例(標準化後) d(東京, 大阪) ≈ 2.4(大都市同士で近い) d(東京, 鳥取) ≈ 6.8(東京の特異性で遠い) d(青森, 秋田) ≈ 0.9(似た地方圏) d(大阪, 神奈川) ≈ 1.7 |
Ward 法ではクラスタを統合したときの分散増加量が最小のペアから結合します。 47 → 1 まで 46 ステップ。
# ステップ 1:青森・秋田 を統合 → 結合距離 ≈ 0.9
# ステップ 2:山形・福島 を統合 → 結合距離 ≈ 1.1
# ...
# ステップ 30:地方圏が大きな塊に → 結合距離 ≈ 4.5
# ステップ 40:都市圏グループ統合 → 結合距離 ≈ 7.2
# ステップ 45:東京が最後まで独立 → 結合距離 ≈ 11.0
# ステップ 46:東京を残りに統合 → 結合距離 ≈ 15.3
結合距離 5.5 のあたりで横線を引くと、 およそ 5 クラスタが得られます:
# cophenetic correlation(共経路相関)
# 元の距離行列とデンドログラムから読み取る距離(共経路距離)の Pearson 相関
# Ward 法:c ≈ 0.72
# 平均連結法:c ≈ 0.78
# 完全連結法:c ≈ 0.65
# 単連結法:c ≈ 0.55
→ 平均連結法が階層構造を最もよく保存しているが、
解釈のしやすさは Ward 法に分がある(球状クラスタを作る)
単位の違う変数(万円、 %、 km² など)をそのまま使うと、 値の大きい変数が距離計算を支配し、 デンドログラムは事実上 1 変数だけで決まってしまいます。 例えば家計支出(千円単位、 数百〜数千)と人口密度(人/km²、 数百〜数千)を組み合わせると、 値域がそろっても分散が違えば結果は歪みます。 必ず StandardScaler または robust scaling で前処理してください。
同じデータでも Ward 法・単連結法・完全連結法・平均連結法で得られるクラスタは全く異なります。 単連結法は「鎖状クラスタ(chaining)」を作りやすく、 全部が 1 つの大クラスタになりがち。 完全連結法は球状を強要する。 Ward 法は分散を最小化するため一般的だが、 解釈は理論的根拠を踏まえて行う必要があります。 複数のリンケージ法を試して結果を比較するのが安全です。
デンドログラムをどの高さで切るか(カット位置)は、 多くの場合分析者の主観に委ねられます。 「縦の枝が長い場所で切る」という経験則はありますが、 数値的根拠としてはシルエット係数・ギャップ統計量・エルボー法などを併用するのが望ましい。 さらに、 ドメイン知識との整合性(5 クラスタが解釈しやすい etc.)も合わせて判断します。
階層クラスタリングは時間計算量 O(n²log n)〜O(n³)、 空間計算量 O(n²)。 n = 47 都道府県なら問題ありませんが、 n = 10000 以上では現実的に計算不能になります。 大規模データには k-means、 mini-batch k-means、 BIRCH、 HDBSCAN など他の手法を検討するか、 サブサンプリングで階層クラスタリングを近似する必要があります。
デンドログラムの葉(個体)の左右の並び順は本質的に任意です。 各内部ノードで左右の子は入れ替え可能なので、 同じ階層構造でも見た目はいくらでも変わります。 「視覚的に隣にあるから類似」と早合点しないでください。 真の距離は結合距離(縦方向の高さ)でしか分かりません。 順序を最適化する手法(optimal leaf ordering、 scipy の `optimal_ordering=True`)もあります。
外れ値(東京のような特異点)があると、 単連結法では「東京 → 大阪 → 神奈川 → ...」のような鎖状クラスタが生まれ、 階層構造が歪みます。 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 28 29 | import pandas as pd import numpy as np from scipy.cluster.hierarchy import linkage, dendrogram, fcluster from scipy.spatial.distance import pdist from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8-sig') X = df[['食料費','教育費','交通費','通信費']].dropna() labels = df.loc[X.index, '都道府県'].values # 標準化 Xs = StandardScaler().fit_transform(X) # リンケージ計算(Ward 法) Z = linkage(Xs, method='ward', metric='euclidean') # デンドログラム描画 plt.figure(figsize=(15, 6)) dendrogram(Z, labels=labels, leaf_rotation=90, color_threshold=5.5) plt.axhline(y=5.5, color='red', linestyle='--', label='cut at 5.5') plt.legend() plt.tight_layout() plt.savefig('dendrogram_ward.png', dpi=150) # 任意の高さで切ってクラスタラベル取得 clusters = fcluster(Z, t=5.5, criterion='distance') result = pd.DataFrame({'都道府県': labels, 'cluster': clusters}) print(result.groupby('cluster')['都道府県'].apply(list)) |
1 2 3 4 5 6 7 8 9 10 11 | from sklearn.cluster import AgglomerativeClustering from sklearn.preprocessing import StandardScaler Xs = StandardScaler().fit_transform(X) # クラスタ数を指定 model = AgglomerativeClustering(n_clusters=5, linkage='ward') labels_pred = model.fit_predict(Xs) result = pd.DataFrame({'都道府県': labels, 'cluster': labels_pred}) print(result.groupby('cluster').size()) |
1 2 3 4 5 6 7 8 9 | from sklearn.cluster import AgglomerativeClustering model = AgglomerativeClustering( n_clusters=None, distance_threshold=5.5, linkage='ward' ) labels_pred = model.fit_predict(Xs) print(f'クラスタ数: {len(set(labels_pred))}') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import seaborn as sns import pandas as pd from sklearn.preprocessing import StandardScaler Xs = pd.DataFrame( StandardScaler().fit_transform(X), index=labels, columns=X.columns ) g = sns.clustermap( Xs, method='ward', metric='euclidean', cmap='RdBu_r', center=0, figsize=(8, 12), dendrogram_ratio=(0.2, 0.1), ) g.savefig('clustermap.png', dpi=150) |
1 2 3 4 5 6 7 8 9 | from scipy.cluster.hierarchy import linkage, cophenet from scipy.spatial.distance import pdist d = pdist(Xs) for method in ['ward', 'average', 'complete', 'single']: Z = linkage(d, method=method) c, _ = cophenet(Z, d) print(f'{method:9s}: 共経路相関 = {c:.4f}') # → 値が高いほど階層構造を保存 |
1 2 3 4 5 6 7 | from sklearn.metrics import silhouette_score for k in range(2, 11): model = AgglomerativeClustering(n_clusters=k, linkage='ward') labs = model.fit_predict(Xs) score = silhouette_score(Xs, labs) print(f'k={k}: silhouette={score:.4f}') |
1 2 3 4 5 | from scipy.cluster.hierarchy import linkage, dendrogram Z = linkage(Xs, method='ward', optimal_ordering=True) # → 隣接する葉が距離的に近くなるように左右を入れ替え dendrogram(Z, labels=labels) |
デンドログラムは「階層クラスタリングの併合過程」を樹形図で示したもの。 縦軸は併合時の距離で、 切る高さによってクラスタ数が決まる。 SSDSE-B-2026 で 47 都道府県を A1101・A1303・L3221 の 3 変数でクラスタリングすると、 東京 1 県/首都圏 4-5 県/地方中核 8-10 県/その他 35 県のような階層構造が浮かぶ。
デンドログラム は「可視化」カテゴリの中核概念。 初めて触れる読者は、 まずこの「🎨 直感」セクションだけ通読し、 必要になった時点で「📐 数式」「🐍 Python」「⚠️ 落とし穴」へ戻る読み方が定着しやすいです。
直感の次は、 厳密な定義を確認します。 数式は言語の一種で、 一度書き慣れれば「言葉より速く伝えられる」便利な道具。 慣れていない方は、 各記号が何を表すかを下の「🔬 記号読み解き」で 1 つずつ確認してください。
上の数式を眺めるだけでは身につかないので、 各記号がどんな役割を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。
数式だけでは「実感」が湧きにくいので、 実データ data/raw/SSDSE-B-2026.csv(47 都道府県 × 16 年)で 1 度手計算してみると理解が定着します。
Ward 法で SSDSE-B-2026 (2023) の標準化済み A1101・A1303・L3221 をクラスタリングすると、 高さ 6.0 付近で 2 クラスタ(東京 vs 残り 46)、 高さ 3.5 で 4 クラスタ(東京/神奈川・大阪・愛知/首都圏/その他)に分かれる。 デンドログラムは併合距離が急増する箇所で切るのが定石。
| 都道府県 | A1101 総人口 | A1303 65 歳以上 | L3221 消費支出 |
|---|---|---|---|
| 東京都 | 14,086,000 | 3,205,000 | 341,320 |
| 神奈川県 | 9,229,000 | 2,390,000 | 306,565 |
| 大阪府 | 8,763,000 | 2,424,000 | 271,246 |
| 愛知県 | 7,477,000 | 1,923,000 | 300,221 |
| 埼玉県 | 7,331,000 | 2,012,000 | 344,092 |
| 千葉県 | 6,257,000 | 1,756,000 | 306,943 |
上記は SSDSE-B-2026 (2023) からの抜粋。 手計算で確認した値が、 後述の Python 実装で得る値と一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。
公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで デンドログラム を動作させます。 まずはこのまま実行してみてください。
# デンドログラム を SSDSE-B-2026 で実行する最小コード
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023] # 2023 年のみ抽出
print(df.shape) # (47, 112)
print(df[['Prefecture','A1101','A1303','L3221']].head())
from scipy.cluster.hierarchy import linkage, dendrogram
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
X = StandardScaler().fit_transform(df[['A1101','A1303','L3221']])
Z = linkage(X, method='ward')
fig, ax = plt.subplots(figsize=(14,5))
dendrogram(Z, labels=df['Prefecture'].values, leaf_rotation=90, color_threshold=4.0, ax=ax)
ax.set_title('SSDSE-B-2026: 47 都道府県 Ward 法')
plt.tight_layout(); plt.savefig('dendrogram_demo.png', dpi=100)
上のコードで動かない場合は、 ①必要なパッケージがインストール済みか(pip install pandas scikit-learn scipy statsmodels matplotlib)、 ②データファイルが data/raw/SSDSE-B-2026.csv に存在するか、 ③encoding='cp932' になっているかを確認してください。
デンドログラム を使うときに初学者が踏みやすい失敗パターン。 1 度経験してしまえば次から避けられますが、 先に知っておくに越したことはありません。