PythonにおけるK-meansクラスタリングの実装と評価手法

K-meansモデルの構築とラベリング

クラスタリング分析を行う場合、まず対象となる特徴量を抽出し、アルゴリズムのパラメータを定義します。scikit-learnのKMeansクラスを利用すると、効率的にモデルを構築できます。

import pandas as pd
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

# データセットの読み込み
dataset = pd.read_csv("transport_data.csv", encoding="utf-8")
feature_cols = ["num_cities", "num_provinces", "distance_km", "complexity_index"]
X = dataset[feature_cols]

# ハイパーパラメータの設定
cluster_count = 5
kmeans_model = KMeans(
    n_clusters=cluster_count,
    init="k-means++",
    n_init=10,
    max_iter=300,
    tol=1e-4,
    random_state=42
)

# モデルの学習
kmeans_model.fit(X)

# 結果の抽出
assigned_clusters = kmeans_model.labels_
cluster_centroids = kmeans_model.cluster_centers_

# 新規データへの適用予測
predicted_labels = kmeans_model.predict(X)

クラスタリング結果の可視化

学習済みモデルの出力結果を把握するため、特徴量の次元数に応じて2Dまたは3Dプロットを行います。Matplotlibを活用し、データポイントと重心を別カラーで描画します。

plt.rcParams["font.family"] = "MS Gothic"
plt.rcParams["axes.unicode_minus"] = False

palette = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd"]

# 3次元プロット(特徴量が3つ以上の場合)
fig = plt.figure(figsize=(8, 6))
ax_3d = fig.add_subplot(111, projection="3d")

for idx in range(cluster_count):
    mask = assigned_clusters == idx
    ax_3d.scatter(
        dataset.loc[mask, "num_cities"],
        dataset.loc[mask, "num_provinces"],
        dataset.loc[mask, "distance_km"],
        color=palette[idx],
        alpha=0.7
    )

ax_3d.scatter(
    cluster_centroids[:, 0],
    cluster_centroids[:, 1],
    cluster_centroids[:, 2],
    marker="*",
    s=200,
    color="black",
    label="Centroids"
)

ax_3d.set_xlabel("都市数")
ax_3d.set_ylabel("通過省份数")
ax_3d.set_zlabel("総距離(km)")
ax_3d.legend()
plt.show()

# 2次元プロット(例:2つの指標)
fig_2d, ax_2d = plt.subplots()
for i in range(cluster_count):
    mask_2d = assigned_clusters == i
    ax_2d.scatter(
        dataset.loc[mask_2d, "num_provinces"],
        dataset.loc[mask_2d, "distance_km"],
        color=palette[i]
    )
ax_2d.scatter(cluster_centroids[:, 1], cluster_centroids[:, 0], 
              marker="x", s=150, color="black")
ax_2d.set_xlabel("通過省份数")
ax_2d.set_ylabel("総距離(km)")
plt.show()

最適なクラスタ数の判定指標

K-meansでは事前に入力するクラスタ数(k)によって結果が大きく変動します。客観的な数値指標を用いて適切なk値を決定する手法を紹介します。

肘方法(Elbow Method)

クラスタ数が増加するに伴う Within-Cluster Distance の減少率を観察します。減少率が緩やかになる「曲がり角」を最適なクラスタ数とみなします。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

data_matrix = dataset[feature_cols].values
manhattan_inertias = []
k_range = range(1, 10)

for n in k_range:
    model_tmp = KMeans(n_clusters=n, random_state=42)
    model_tmp.fit(data_matrix)
    
    cluster_dist_sum = 0.0
    for cluster_id in range(n):
        pts_in_cluster = data_matrix[model_tmp.labels_ == cluster_id]
        center = model_tmp.cluster_centers_[cluster_id]
        # ベクトル演算によるマンハッタン距離の集計
        cluster_dist_sum += np.sum(np.abs(pts_in_cluster - center))
    
    manhattan_inertias.append(cluster_dist_sum)

plt.figure()
plt.plot(k_range, manhattan_inertias, marker="o", linestyle="--")
plt.xlabel("クラスタ数 k")
plt.ylabel("マンハッタン距離の総和")
plt.grid(True)
plt.show()

輪郭係数(Silhouette Score)

各データポイントが自身のクラスタ内でどの程度密に集まり、他のクラスタとどの程度離れているかを-1~1の範囲で評価します。値が高いほど分離性能が良いと判断できます。

from sklearn.metrics import silhouette_score

silhouette_values = []
eval_range = range(2, 10)

for k_val in eval_range:
    clf = KMeans(n_clusters=k_val, n_init=10, random_state=42)
    clf.fit(data_matrix)
    score = silhouette_score(data_matrix, clf.labels_)
    silhouette_values.append(score)

plt.figure()
plt.plot(eval_range, silhouette_values, marker="s", color="#2ca02c")
plt.xlabel("クラスタ数 k")
plt.ylabel("輪郭係数")
plt.title("クラスタ数に対する分離度評価")
plt.grid(True)
plt.show()

タグ: Python K-Means Scikit-learn cluster-analysis unsupervised-learning

8月1日 17:12 投稿