見出し画像

「入門はじめての多変量解析」をPythonで写経 Vol.27 ~ 6章「はじめてのクラスター分析」さまざまなクラスタリング

6章「はじめてのクラスター分析」

書籍の著者 石村貞夫 先生、石村光資郎 先生


書籍「入門はじめての多変量解析」6章「はじめてのクラスター分析」の Python写経活動記録 です。 

多変量解析の入門を Python と一緒に学ぶ写経シリーズです。

クラスター分析はいわゆる「クラスタリング」タスクです。
重回帰分析・判別分析のような「目的変数・教師データ」を使わず、個々のデータの「類似度合い」に基づいて「グループ分け」する手法です。

この記事は、書籍の 階層的クラスタリング と、書籍非掲載の 非階層的クラスタリング を Python で実装して楽しみます!

ChatGPT 活用型学習で進めてまいります!
では書籍を開いて多変量解析の旅に出かけましょう🚀

男性アイドルグループのイラスト:「いらすとや」さんより

はじめに


このブログシリーズは、書籍「入門はじめての多変量解析」(東京図書、「テキスト」と呼びます)の Python 写経を通じて得た「多変量解析の楽しさ」をご紹介します。

書籍の紹介と引用表記はリンク先の記事に掲載しています。

6章 はじめてのクラスター分析


この記事は6章の以下のSectionを取り扱います。

6.1 似ている!似ていない?
6.2 クラスタ間の距離の決め方
6.3 クラスター分析の手順は?
6.4 デンドログラムの使い方

記事に用いるデータは、テキストに掲載されたデータそのものを引用しています。
データ件数の少ないものはコード上でデータを登録し、データ件数の多いものはCSVファイル化してデータを読み込みしています。

この記事で用いるライブラリをインポートします。

### インポート

# 数値計算
import string
import numpy as np
import pandas as pd

# 階層型クラスタリング、デンドログラム
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster

# 描画
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.family'] = 'Meiryo'  # または import japanize_matplotlib

階層的クラスタリングの概要

データの「似た者どうしを1つのグループに仕分け」するのがクラスタリングのお仕事です。
このグループのことを「クラスタ」と呼びます。

階層的クラスタリングは「トーナメント表」に似た構造でデータ点をグループ分け=クラスタの形成を行います。

テキストの例題データを用いて「トーナメント表」ライクなクラスタ形成を体感してみましょう。

■ データの設定
テキスト p.226 表 6.3.1 の例題データを引用します。

### 国別データ p.226 表6.3.1

# 設定と準備
N = 11  # 標本サイズ
countries = list(string.ascii_uppercase[:N])  # 国名リスト

# データフレームの作成
data1 = pd.DataFrame(
    {'患者': [6.6, 8.4, 24.2, 10.0, 14.5, 12.2, 4.8, 19.8, 6.1, 26.8, 7.4],
     '新聞': [35.8, 22.1, 19.1, 34.4, 9.9, 31.1, 53.0, 7.5, 53.4, 50.0, 42.1]},
     index=countries)
data1.index.name = '国名'

# 結果の表示
data1

【実行結果】
11 か国のある病気の患者数と新聞発行数データです。

■ 散布図で確認
テキスト p.227 図 6.3.1 に相当する散布図を描画しましょう。

### データの散布図 p.227 図6.3.1

# 散布図の描画
sns.scatterplot(data=data1, x='患者', y='新聞', s=80)
# 国名のテキストの描画
for x, y, s in zip(data1['患者'], data1['新聞'], data1.index):
    plt.text(x=x+0.3, y=y+0.2, s=s)
# 修飾
plt.xlabel('患者数 [人]', fontsize=12)
plt.ylabel('新聞発行部数 [部]', fontsize=12);

【実行結果】
このような分布の中で「似たものデータどうし」をクラスタに分けていきます!

■ デンドログラムの描画
デンドログラムは「トーナメント表」ライクな図です。
seaborn の clustermap で描画できます。

## クラスタマップ
sns.clustermap(
    data1.T,             # 行・列を転置
    method='centroid',   # 重心法
    metric='euclidean',  # ユークリッド距離 ※重心法で二乗ユークリッド距離を使えない
    z_score=0,           # データの標準化 ※0は行単位。転置しているので実質は列単位
    figsize=(5, 4),
    row_cluster=False,
    dendrogram_ratio=(0.1, 0.7),
    cmap='Blues',
);

【実行結果】
2つの変数の大小を比べやすくする目的で、データの標準化を実施しています。

「トーナメント表の線」が似たものを結んでいます。
距離が近いものから線を結ぶ=クラスタを形成しています。
トーナメント表の下部から上部にかけて、クラスタが大きく育つイメージです。

クラスタマップのいいところは、データの状態とクラスタ形成の傾向が見えることにあります。

最もクラスタ形成が遅い「 J 」国は患者・新聞ともに大きな値です。
その右隣の「C, E, H」国は、患者多め、新聞少なめの傾向があります。
その右隣の「G, I」国は、患者少なめ、新聞多めの傾向があります。
残りの「B, D, F, A, K」国は、患者・新聞ともに中程度、のようです。

■ クラスタリングの結果をざっくり確認
クラスタリング結果の雰囲気を体感しましょう。
ひとまずコード内容を置いておき、出力結果に集中します。

### データの散布図 p.227 図6.3.1 centroid:重心法、euclidean:ユークリッド距離

## クラスタリングの実行
# リンケージ関数の実行
Z = linkage(data1, method='centroid', metric='euclidean')
# クラスタラベルの取得
labels = fcluster(Z, t=4, criterion='maxclust')

## 描画
# 散布図の描画
sns.scatterplot(data=data1, x='患者', y='新聞', s=80, hue=labels, 
                palette = ['tab:blue', 'tab:orange', 'tab:green', 'tab:red'])
# 国名のテキストの描画
for x, y, s in zip(data1['患者'], data1['新聞'], data1.index):
    plt.text(x=x+0.3, y=y+0.2, s=s)
# 修飾
plt.xlabel('患者数 [人]', fontsize=12)
plt.ylabel('新聞発行部数 [部]', fontsize=12)
plt.legend(title='クラスタ番号', bbox_to_anchor=(1, 1))
plt.grid(lw=0.5, alpha=0.5);

【実行結果】

同じ色が同じクラスタです。
4つのクラスタを作りましたので、4色あります。
確かに近いデータどうしでクラスタを形成しています!

では、階層型クラスタリングの詳細に進みましょう。

2つの距離の概念

「似たもの」とは「距離が近いもの」です。
クラスタリングは2つの距離の測り方で特徴づけられます。

1️⃣ 距離の算出方法
ある点とある点の間の「長さの測り方」です。
テキストは p.219 で以下の方法を例示しています。

  • ユークリッド距離

  • ユークリッド距離の2乗

  • マハラノビス距離

  • 相関係数

この記事は「ユークリッド距離」を用います。
ユークリッド距離は「三平方の定理」のような測り方です。

2️⃣ クラスタ間の測り方
データ1点どうしの距離はデータ点を起点にして測れます。
ところがグループとしてのクラスタはどこを起点にすればよいでしょう。
この起点などを定めているのがクラスタ間の測り方です。

テキストは p.221 で以下の方法を例示しています。

  1. 最短距離法

  2. 最長距離法

  3. 群平均法

  4. メディアン法

  5. 重心法

  6. ウォード法

この記事ではこれらの方法を試してみようと思います。

文章だけではわかりにくいですね!
テキストの図やWebサイトで直感的な「測り方」を確認してください!

【Web サイトの参考例】
解説がとても分かりやすいのでおすすめです。
ありがとうございます!

テキストの階層的クラスタリングステップを体感する

テキストの p.228~235 では、SPSS の階層クラスター分析に準拠して、「重心法」&「ユークリッド距離の2乗」によるクラスタリングの計算ステップを1つ1つ進めるスタイルをとっています。

この計算ステップを Python でやってみましょう。

クラスタリングシミュレーター関数を定義します。
「重心法」と「ユークリッド距離の2乗」を用いています。

## 簡易なクラスタリングシミュレーター関数(重心法、二乗ユークリッド距離)の定義
def cluster_step(step, base_df, step_df, row1, row2):
    
    # データフレームのコピー
    tmp_df = step_df.copy()

    # step2以降は2つのクラスタの結合を実施
    if step >= 2:
        # 指定した2つの行を削除
        tmp_df = tmp_df.drop(index=[row1, row2])
        # 結合対象の行のリスト化
        row_list = sorted(list(row1) + list(row2))
        # 結合後の行を追加(重心法)
        tmp_df.loc[''.join(row_list)] = base_df.loc[row_list].mean(axis=0).values
        # インデックスをアルファベット順に並び替え
        tmp_df = tmp_df.sort_index()

    # 距離を格納する配列の初期化(inf:無限大を設定)
    dist = np.zeros((N-step, N-step))
    dist[:, :] = np.inf

    # 距離の算出 ※ユークリッド距離の二乗
    for i in range(N-step):
        for j in range(i+1, N+1-step):
            dist[i, j-1] = sum((tmp_df.iloc[i] - tmp_df.iloc[j])**2)

    # 結果をデータフレーム化して表示
    idx = tmp_df.index
    dist_df = pd.DataFrame(dist, index=idx[:-1], columns=idx[1:])
    display(
        dist_df.style.highlight_min(axis=None, color='lightpink')
        .format(precision=1)
    )

    # 最小値のインデックス取得
    idx1, idx2 = np.unravel_index(np.argmin(dist), dist.shape)
    # 次の結合の行名の取得
    next1, next2 = idx[idx1], idx[idx2+1]
    # 結合後の行名の作成
    join_row = ''.join(sorted(list(next1) + list(next2)))

    # 戻り値:結合後のdf, 次の結合対象行next1, next2, 結合後join_row, 距離
    return tmp_df, next1, next2, join_row, dist[idx1, idx2]

【実行結果】なし

簡単な準備をします。
手順1つ1つの履歴を残すリストの初期化です。

# 実行準備
cluster_result = []

【実行結果】なし

では手順 10 まで1つ1つ計算していきます!

1️⃣ 手順1
A から K の各データ点を1つのクラスタとみなして、クラスタ間の距離を計算します。

# 手順1
tmp_df, row1, row2, j_row, min_d = cluster_step(1, data1, data1.copy(), '', '')
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
最短距離は G と I の 1.8 です。
次のステップでは G と I を併合します。

2️⃣ 手順2
どんどんクラスタ間の距離を計算していきます。
手順3以降も同様です。

# 手順2
tmp_df, row1, row2, j_row, min_d = cluster_step(2, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
次のステップでは B と D を併合します。

以下、淡々と手順をこなしていきます!

3️⃣ 手順3

# 手順3
tmp_df, row1, row2, j_row, min_d = cluster_step(3, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
次のステップでは AD と F を併合します。

4️⃣ 手順4

# 手順4
tmp_df, row1, row2, j_row, min_d = cluster_step(4, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
次のステップでは E と H を併合します。

5️⃣ 手順5

# 手順5
tmp_df, row1, row2, j_row, min_d = cluster_step(5, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
次のステップでは ADF と K を併合します。

6️⃣ 手順6

# 手順6
tmp_df, row1, row2, j_row, min_d = cluster_step(6, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
次のステップでは C と EH を併合します。

7️⃣ 手順7

# 手順7
tmp_df, row1, row2, j_row, min_d = cluster_step(7, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
次のステップでは ADFK と CEH を併合します。

8️⃣ 手順8

# 手順8
tmp_df, row1, row2, j_row, min_d = cluster_step(8, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
次のステップでは ABDFK と GI を併合します。

9️⃣ 手順9

# 手順9
tmp_df, row1, row2, j_row, min_d = cluster_step(9, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】
次のステップでは CEH と J を併合します。

🔟 手順 10 (ラスト)

# 手順10
tmp_df, row1, row2, j_row, min_d = cluster_step(10, data1, tmp_df, row1, row2)
cluster_result.append([row1, row2, j_row, min_d])
print(row1, row2, min_d)

【実行結果】

最後のクラスタ形成が完了しました。
クラスタ形成の履歴を順を追って振り返りましょう。

# クラスター形成順の再表示
pd.DataFrame(
    cluster_result,
    columns=['クラスタ1', 'クラスタ2', '親クラスタ', '距離']
).round(2)

【実行結果】
クラスタ1とクラスタ2が併合して親クラスタになる、そんな感じです。

ちなみに、Python の主要な階層的クラスタリングにおいて、「重心法」では「ユークリッド距離の2乗」を扱えない模様です。
残念…

さまざまなクラスタ間距離の階層的クラスタリング

今度は scipy を利用して、さまざまなクラスタ間距離の計算方法で階層的クラスタリングをサクッと実践します!
デンドログラムやクラスタ別散布図を描きましょう。
クラスタ間距離の計算方法の特徴に少しでも近づけるといいですね🍀

scipy の階層的クラスタリングは次の順序で実装します。

  • リンケージ関数 linkage() でクラスタの元ネタを作成

  • リンケージ関数の結果を使って…

    • dendrogram() でデンドログラムを描画

    • fcluster() でクラスタを確定

クラスタリングの重要なハイパーパラメータは「クラスタ数」です。
アルゴリズムが自動的にクラスタの数を設定するのではありません。
今回はテキスト p.236~ に合わせて「クラスタ数=4」とします。

2点の距離は「ユークリッド距離」で測ります。

クラスタ別散布図描画のヘルパー関数を作っておきます。

## 散布図描画ヘルパー関数の定義
def plot_cluster(labels):
    
    # 色の設定
    palette = ['tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'purple',
               'gold', 'tomato', 'silver', 'lightpink', 'darkred']
    
    # 散布図の描画
    sns.scatterplot(data=data1, x='患者', y='新聞', s=80, hue=labels,
                    palette=palette[:np.unique(labels).size])
    # 国名のテキストの描画
    for x, y, s in zip(data1['患者'], data1['新聞'], data1.index):
        plt.text(x=x+0.3, y=y+0.2, s=s)
    # 修飾
    plt.xlabel('患者数 [人]', fontsize=12)
    plt.ylabel('新聞発行部数 [部]', fontsize=12)
    plt.legend(title='クラスタ番号', bbox_to_anchor=(1, 1))
    plt.grid(lw=0.5, alpha=0.5)
    plt.show()

【実行結果】なし

📈 ウォード法
書籍やWebサイトの情報にはウォード法を薦めるものが多いので、まずはウォード法から。
クラスタを併合するときの情報損失量を距離にしてクラスタ併合する方法です。

◆ クラスタリング&デンドログラム描画
クラスタが4つになるよう、赤い水平破線で「切り取って」います。

### 6. ウォード法 ward
## デンドログラムの描画 p.236 図6.4.1

# 区切り線の位置
thres = 20
# リンケージ関数の実行
Z = linkage(data1, method='ward', metric='euclidean')
# デンドログラムの描画
dn = dendrogram(Z, labels=data1.index, color_threshold=thres)
# クラスタ数を4に区切る点線の描画
plt.axhline(thres, color='tab:red', ls='--');

【実行結果】

◆ クラスタ間の距離
リンケージ関数の結果データにクラスタ併合や距離の情報が含まれています。
表示してみましょう。

# クラスタ間の距離の表示 ※Zに距離の情報が含まれている

# データフレーム化
cluster1_df = pd.DataFrame({
    'クラスタ1': [z if z>=N else countries[z] for z in Z[:, 0].astype(int)],
    'クラスタ2': [z if z>=N else countries[z] for z in Z[:, 1].astype(int)],
    '親クラスタ': np.arange(N, N+len(Z)),
    '距離': Z[:, 2],
    'クラスタ内のデータ数': Z[:, 3].astype(int),
})
# 結果の表示
cluster1_df.round(2)

【実行結果】

◆ クラスタ別散布図の描画
先ほど作ったヘルパー関数でクラスタ別散布図を描画します。

# データの散布図と4つのクラスタ p.237 図6.4.2
labels = fcluster(Z, t=4, criterion='maxclust')
plot_cluster(labels)

【実行結果】
このウォード法のクラスタを基準にして、他の方法のクラスタとの違いを確認していきましょう。

📈 最短距離法(単純連結法)
クラスタどうしの最も近いデータ点の距離でクラスタ併合する方法です。

◆ クラスタリング&デンドログラム描画

### 1. 最短距離法 single(単純連結法)
## デンドログラムの描画 p.236 図6.4.1

# 区切り線の位置
thres = 12
# リンケージ関数の実行
Z = linkage(data1, method='single', metric='euclidean')
# デンドログラムの描画
dn = dendrogram(Z, labels=data1.index, color_threshold=thres)
# クラスタ数を4に区切る点線の描画
plt.axhline(thres, color='tab:red', ls='--');

【実行結果】

◆ クラスタ別散布図の描画

# データの散布図と4つのクラスタ p.237 図6.4.2
labels = fcluster(Z, t=4, criterion='maxclust')
plot_cluster(labels)

【実行結果】
ウォード法と比べて、単純な距離の近さでクラスタができています!

(参考:ウォード法)

📈 最長距離法(完全連結法)
クラスタどうしの最も遠いデータ点の距離でクラスタ併合する方法です。

◆ クラスタリング&デンドログラム描画

### 2. 最長距離法 complete(完全連結法)
## デンドログラムの描画 p.236 図6.4.1

# 区切り線の位置
thres = 17
# リンケージ関数の実行
Z = linkage(data1, method='complete', metric='euclidean')
# デンドログラムの描画
dn = dendrogram(Z, labels=data1.index, color_threshold=thres)
# クラスタ数を4に区切る点線の描画
plt.axhline(thres, color='tab:red', ls='--');

【実行結果】

◆ クラスタ別散布図の描画

# データの散布図と4つのクラスタ p.237 図6.4.2
labels = fcluster(Z, t=4, criterion='maxclust')
plot_cluster(labels)

【実行結果】
ウォード法と比べて「K」が異なっています。

(参考:ウォード法)

📈 群平均法
すべてのデータ間の距離の平均でクラスタ併合する方法です。

◆ クラスタリング&デンドログラム描画

### 3. 群平均法 average
## デンドログラムの描画 p.236 図6.4.1

# 区切り線の位置
thres = 17
# リンケージ関数の実行
Z = linkage(data1, method='average', metric='euclidean')
# デンドログラムの描画
dn = dendrogram(Z, labels=data1.index, color_threshold=thres)
# クラスタ数を4に区切る点線の描画
plt.axhline(thres, color='tab:red', ls='--');

【実行結果】

◆ クラスタ別散布図の描画

# データの散布図と4つのクラスタ p.237 図6.4.2
labels = fcluster(Z, t=4, criterion='maxclust')
plot_cluster(labels)

【実行結果】
ウォード法と同じクラスタが形成されました。

(参考:ウォード法)

📈 メディアン法
すべてのデータ間の距離の中央値でクラスタ併合する方法です。

◆ クラスタリング&デンドログラム描画

### 4. メディアン法 median
## デンドログラムの描画 p.236 図6.4.1

# 区切り線の位置
thres = 15.5
# リンケージ関数の実行
Z = linkage(data1, method='median', metric='euclidean')
# デンドログラムの描画
dn = dendrogram(Z, labels=data1.index, color_threshold=thres)
# クラスタ数を4に区切る点線の描画
plt.axhline(thres, color='tab:red', ls='--');

【実行結果】

◆ クラスタ別散布図の描画

# データの散布図と4つのクラスタ p.237 図6.4.2
labels = fcluster(Z, t=4, criterion='maxclust')
plot_cluster(labels)

【実行結果】
ウォード法と同じクラスタが形成されました。

(参考:ウォード法)

📈 重心法
クラスタの重心の距離でクラスタ併合する方法です。

◆ クラスタリング&デンドログラム描画

### 5. 重心法 centroid
## デンドログラムの描画 p.236 図6.4.1

# 区切り線の位置
thres = 17
# リンケージ関数の実行
Z = linkage(data1, method='centroid', metric='euclidean')
# デンドログラムの描画
dn = dendrogram(Z, labels=data1.index, color_threshold=thres)
# クラスタ数を4に区切る点線の描画
plt.axhline(thres, color='tab:red', ls='--');

【実行結果】

◆ クラスタ別散布図の描画

# データの散布図と4つのクラスタ p.237 図6.4.2
labels = fcluster(Z, t=4, criterion='maxclust')
plot_cluster(labels)

【実行結果】
ウォード法と同じクラスタが形成されました。

(参考:ウォード法)

📈 重み付き平均法
群平均法の距離をさらにクラスタサイズで重み付けした距離でクラスタ併合する方法です。

◆ クラスタリング&デンドログラム描画

### x. 重み付き平均法 weighted
## デンドログラムの描画 p.236 図6.4.1

# 区切り線の位置
thres = 17
# リンケージ関数の実行
Z = linkage(data1, method='weighted', metric='euclidean')
# デンドログラムの描画
dn = dendrogram(Z, labels=data1.index, color_threshold=thres)
# クラスタ数を4に区切る点線の描画
plt.axhline(thres, color='tab:red', ls='--');

【実行結果】

◆ クラスタ別散布図の描画

# データの散布図と4つのクラスタ p.237 図6.4.2
labels = fcluster(Z, t=4, criterion='maxclust')
plot_cluster(labels)

【実行結果】
ウォード法と同じクラスタが形成されました。

(参考:ウォード法)

さまざまなクラスタ間距離の算出方法の特徴、なんとなく見えてきましたか?

さまざまなクラスタリング手法

階層的クラスタリングを実装できる Python ライブラリは他にもあります。
さらにクラスタリング手法には、階層的クラスタリングのほかに「非階層的クラスタリング」の手法があります。

Python でいろんなクラスタリングを満喫しましょう!

1️⃣ scikit-learn の階層的クラスタリング
AgglomerativeClustering() で階層的クラスタリングを実装できます。

ウォード法・ユークリッド距離によるクラスタリング結果から。

## 凝集的階層クラスタリング

# 追加インポート
from sklearn.cluster import AgglomerativeClustering

# モデルの学習・予測
ac = AgglomerativeClustering(metric='euclidean', linkage='ward', n_clusters=4)
y_pred = ac.fit_predict(data1)

# 散布図の描画
plot_cluster(y_pred)

【実行結果】
scipy の結果と同じです(当たり前!?)

デンドログラムを描画しましょう。
scikit-learn公式サイトのデンドログラム描画関数を引用します。

# デンドログラム描画関数 by scikit-learn
# https://scikit-learn.org/stable/auto_examples/cluster/plot_agglomerative_dendrogram.html

def plot_dendrogram(model, **kwargs):
    # Create linkage matrix and then plot the dendrogram

    # create the counts of samples under each node
    counts = np.zeros(model.children_.shape[0])
    n_samples = len(model.labels_)
    for i, merge in enumerate(model.children_):
        current_count = 0
        for child_idx in merge:
            if child_idx < n_samples:
                current_count += 1 # leaf node
            else:
                current_count += counts[child_idx - n_samples]
        counts[i] = current_count

    linkage_matrix = np.column_stack(
        [model.children_, model.distances_, counts]
    ).astype(float)
    
    # Plot the corresponding dendrogram
    dendrogram(linkage_matrix, **kwargs)

【実行結果】なし

デンドログラムを描画します。

## デンドログラムの描画 p.236 図6.4.1 scikit-learn利用

# 区切り線の位置
thres = 20

# 凝集的クラスタリングの実行(デンドログラム用) ウォード法、ユークリッド距離
model_dg = AgglomerativeClustering(
    metric='euclidean', linkage='ward', distance_threshold=0, n_clusters=None
).fit(data1)

# デンドログラムの描画
plot_dendrogram(model_dg, truncate_mode='lastp', p=11, labels=data1.index,
                color_threshold=thres)
plt.axhline(thres, color='tab:red', ls='--');

【実行結果】

クラスタ間の距離情報も見ることができます。

# クラスタ間の距離の表示 
cluster1_sk_df = pd.DataFrame({
    'クラスタ1': [z if z>=N else countries[z] for z in model_dg.children_[:, 0]],
    'クラスタ2': [z if z>=N else countries[z] for z in model_dg.children_[:, 1]],
    '親クラスタ': np.arange(N, N+len(Z)),
    '距離': model_dg.distances_})
cluster1_sk_df.round(2)

【実行結果】
scipy の結果と同じです(当たり前!?)

次のアルゴリズムから「非階層的クラスタリング」になります。
デンドログラムは無いです。
アルゴリズムの詳しい説明も無いです。
クラスタリングアルゴリズムと言い難いものも含まれますが…
クラスタ形成可能なものたちです。

2️⃣ k-means 法
クラスタ数を指定します。

## k-means法

# 追加インポート
from sklearn.cluster import KMeans

# クラスタリングの実行
kmeans = KMeans(n_clusters=4, random_state=0)
kmeans.fit(data1)

# 散布図の描画
plot_cluster(kmeans.labels_)

【実行結果】
階層的クラスタリング・ウォード法と同じクラスタが形成されました。

3️⃣ DBSCAN
クラスタ数を指定しません。
引数 eps でクラスタ形成をコントロールするようです。

## DBSCAN

# 追加インポート
from sklearn.cluster import DBSCAN

# クラスタリングの実行
dbscan = DBSCAN(eps=15)
dbscan.fit(data1)

# 散布図の描画
plot_cluster(dbscan.labels_)

【実行結果】
2つのクラスタが形成されました。

4️⃣ GaussianMixture
混合正規分布をモデリングするアルゴリズムです。
クラスタ数(混合分布の数)を指定します。

## GaussianMixture

# 追加インポート
from sklearn.mixture import GaussianMixture

# モデルの学習・予測
gm = GaussianMixture(n_components=4, random_state=1)
y_pred = gm.fit_predict(data1)

# 散布図の描画
plot_cluster(y_pred)

【実行結果】
「B」が独立、「J」が「G・I」と併合したようです。
不思議な組み合わせですね…

クラスタリングは以上です!
お手元のデータでぜひクラスタリングを試して下さい。
新しい視点が開けるかもです。


記事の最後はChatGPTが締めくくります。
まとまりを見つけて、まとめてくれています。

📘 ChatGPTのひとこと:

今回はクラスター分析で、「似たもの同士」を自分の手で見つける楽しさを味わいました。階層的クラスタリングではいろいろな距離感を試しながら木を育てるようにグループを構築し、非階層的クラスタリングではまるでパズルのピースを当てはめるように塊を探しました😊

データを「分ける」のではなく「まとまりを見つける」視点を手に入れたことで、データの世界にまた新しい色合いが加わったはずです✨

次回は数量化Ⅰ類の世界へ進みます。データを「数値でありながらカテゴリとして扱う」感覚を学びながら、また一緒に静かなワクワクを共有しましょう🌱

今回の写経は以上です。


シリーズの記事

次の記事

前の記事

目次

ブログの紹介


note で7つのシリーズ記事を書いています。
ぜひ覗いていってくださいね!

1.のんびり統計

統計検定2級の問題集を手がかりにして、確率・統計をざっくり掘り下げるブログです。
雑談感覚で大丈夫です。ぜひ覗いていってくださいね。
統計検定2級公式問題集CBT対応版に対応しています。
Python、EXCELのサンプルコードの配布もあります。

2.実験!たのしいベイズモデリング1&2をPyMC Ver.5で

書籍「たのしいベイズモデリング」・「たのしいベイズモデリング2」の心理学研究に用いられたベイズモデルを PyMC Ver.5で描いて分析します。
この書籍をはじめ、多くのベイズモデルは R言語+Stanで書かれています。
PyMCの可能性を探り出し、手軽にベイズモデリングを実践できるように努めます。
身近なテーマ、イメージしやすいテーマですので、ぜひぜひPyMCで動かして、一緒に楽しみましょう!

3.実験!岩波データサイエンス1のベイズモデリングをPyMC Ver.5で

書籍「実験!岩波データサイエンスvol.1」の4人のベイジアンによるベイズモデルを PyMC Ver.5で描いて分析します。
この書籍はベイズプログラミングのイロハをざっくりと学ぶことができる良書です。
楽しくPyMCモデルを動かして、ベイズと仲良しになれた気がします。
みなさんもぜひぜひPyMCで動かして、一緒に遊んで学びましょう!

4.楽しい写経 ベイズ・Python等

ベイズ、Python、その他の「書籍の写経活動」の成果をブログにします。
主にPythonへの翻訳に取り組んでいます。
写経に取り組むお仲間さんのサンプルコードになれば幸いです🍀

5.RとStanではじめる心理学のための時系列分析入門 を PythonとPyMC Ver.5 で

書籍「RとStanではじめる心理学のための時系列分析入門」の時系列分析をPythonとPyMC Ver.5 で実践します。
この書籍には時系列分析のテーマが盛りだくさん!
時系列分析の懐の深さを実感いたしました。
大好きなPythonで楽しく時系列分析を学びます。

6.データサイエンスっぽいことを綴る

統計、データ分析、AI、機械学習、Pythonのコラムを不定期に綴っています。
統計・データサイエンス書籍にまつわる記事が多いです。
「統計」「Python」「数学とPython」「R」のシリーズが生まれています。

7.Python機械学習プログラミング実践記

書籍「Python機械学習プログラミング PyTorch & scikit-learn編」を学んだときのさまざまな思いを記事にしました。
この書籍は、scikit-learnとPyTorchの教科書です。
よかったらぜひ、お試しくださいませ。

最後までお読みいただきまして、ありがとうございました。

いいなと思ったら応援しよう!

ネイピア DS 応援ありがとうございます。これからもがんばって記事を作成します!

この記事が参加している募集