データ分析でクラスタリングを行う際、多くの人が最初に学ぶのが「K-Means」ではないでしょうか。しかし、K-Meansには「事前にクラスタ数を決めなければならない」という大きな制約があります。
「そもそもデータが何個のグループに分かれるか分からない…」
そんな場面で非常に強力な選択肢となるのが、今回解説する**MeanShift(ミーンシフト)**クラスタリングです。
この記事では、MeanShiftがK-Meansとどう違うのかという比較を軸に、PythonのScikit-learnを使った具体的な実装方法まで、初心者にも分かりやすく徹底解説します。
はじめに:クラスタ数を”決め打ち”しないMeanShiftクラスタリング
まず、MeanShiftがどのようなアルゴリズムなのか、その核心となる考え方を理解しましょう。
MeanShiftの基本的な考え方:密度の高い中心へ
MeanShiftは、データの密度に着目したアルゴリズムです。各データ点が、自身の周辺で最もデータが密集している方向(平均=Mean)へ少しずつ移動(Shift)していく、という処理を繰り返します。
最終的に、各データ点はどこかの「密度のピーク」に収束します。そして、同じピークにたどり着いたデータ点同士を同じクラスタと見なします。これにより、データが自然に形成しているグループを発見できるのです。
「クラスタ数が事前にわからない」場面で活躍
このアルゴリズムの特性上、K-Meansのように「データを3つのクラスタに分けてください」といった指示は必要ありません。データ自身の分布に基づいて、適切なクラスタの数を自動で見つけ出してくれます。
そのため、探索的なデータ分析の初期段階など、データ構造が未知の状態でクラスタリングを試みたい場合に特に有効です。
この記事で学べること
- MeanShiftとK-Meansの根本的な違い
- それぞれのアルゴリズムの長所と短所
- Python (
Scikit-learn) を使ったMeanShiftの実装方法 - 結果の可視化と解釈のポイント
MeanShift vs K-Means:決定的な3つの違い
それでは、MeanShiftとK-Meansの具体的な違いを3つのポイントで比較してみましょう。
違い①:クラスタ数(k)の事前指定
これが両者の最も大きな違いです。
- K-Means: 必要。アルゴリズムを実行する前に、人間がクラスタ数(k)を指定する必要があります。適切なkを見つけるために、エルボー法などの追加的な分析が必要になることもあります。
- MeanShift: 不要。データの分布から自動的にクラスタ数を決定します。その代わり、後述する
bandwidth(バンド幅)というパラメータが重要になります。
違い②:クラスタの形状
アルゴリズムの性質上、得意なクラスタの形状が異なります。
- K-Means: 各クラスタが**球状(円形)**であることを前提としています。そのため、細長いクラスタや複雑な形のクラスタの検出は苦手です。
- MeanShift: 密度のピークを探すため、任意の形状のクラスタを発見できる可能性があります。球状でないデータセットに対して、より柔軟に対応できます。
違い③:計算コストとスケーラビリティ
大規模なデータセットを扱う場合、計算速度は重要な要素です。
- K-Means: 計算量が少なく、非常に高速に動作します。大規模なデータセットにも適用しやすい(スケーラビリティが高い)です。
- MeanShift: 各データ点について近傍の点を探索するため、データ数が多くなると計算コストが急激に増加する傾向があります。大規模データセットへの適用は注意が必要です。
PythonでMeanShiftを動かしてみよう
違いを理解したところで、実際にScikit-learnを使ってMeanShiftを実装してみましょう。
必要なライブラリとサンプルデータの準備
まずは、必要なライブラリをインポートし、クラスタリング用のサンプルデータをmake_blobsで生成します。
import numpy as np
from sklearn.cluster import MeanShift, estimate_bandwidth
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
# K-Meansとの比較用にインポート
from sklearn.cluster import KMeans
# サンプルデータの生成 (中心点を指定)
centers = [[-2, 3], [1, 4], [3, 0]]
X, _ = make_blobs(n_samples=1500, centers=centers, cluster_std=0.8)
# データをプロットして確認
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 1], s=10, alpha=0.7)
plt.title('Generated Sample Data')
plt.grid(True)
plt.show()最重要パラメータbandwidthを自動で推定する
MeanShiftにはbandwidthという、密度の中心を探す際の「探索範囲の広さ」を決める重要なパラメータがあります。幸い、Scikit-learnにはデータから適切な値を推定してくれるestimate_bandwidth関数が用意されています。
# データセットからbandwidthを自動推定
# quantileパラメータで推定の感度を調整できます
bandwidth = estimate_bandwidth(X, quantile=0.2, n_samples=500)
print(f"自動推定されたBandwidth: {bandwidth:.3f}")この値が小さいとクラスタが細分化され、大きいと統合されやすくなります。まずは自動推定から始めるのが良いでしょう。
MeanShiftモデルの学習とラベルの取得
推定したbandwidthを使って、MeanShiftモデルを作成し、fit_predictメソッドで学習とクラスタラベルの予測を同時に行います。
# MeanShiftモデルのインスタンスを作成
ms = MeanShift(bandwidth=bandwidth, bin_seeding=True)
# 学習とクラスタリングの実行
labels = ms.fit_predict(X)
# 発見されたクラスタの数を取得
n_clusters_ = len(np.unique(labels))
print(f"MeanShiftが見つけたクラスタ数: {n_clusters_}")データ生成時に3つの中心を指定しましたが、MeanShiftが正しく3つのクラスタを発見できていることがわかります。
クラスタリング結果を可視化して比較する
最後に、MeanShiftがどのようにデータをグループ分けしたのかを可視化して確認します。
MeanShiftの実行結果をプロット
所属するクラスタごとにデータ点を色分けしてプロットします。
plt.figure(figsize=(10, 8))
colors = ['#ff7f0e', '#2ca02c', '#1f77b4', '#d62728', '#9467bd']
# ユニークなラベル(クラスタ番号)を取得
unique_labels = np.unique(labels)
for k, col in zip(unique_labels, colors):
# 同じクラスタに属するデータ点のインデックスを取得
class_member_mask = (labels == k)
xy = X[class_member_mask]
plt.scatter(xy[:, 0], xy[:, 1], c=col, s=20, edgecolor='k', label=f'Cluster {k}')
plt.title(f'MeanShift Clustering Results (Clusters: {n_clusters_})')
plt.grid(True)発見されたクラスタ中心を確認する
MeanShiftが見つけたクラスタの中心(密度のピーク)をプロットに追加します。
# MeanShiftが見つけたクラスタ中心
cluster_centers = ms.cluster_centers_
# クラスタ中心をプロット
plt.scatter(cluster_centers[:, 0], cluster_centers[:, 1], c='black', s=200, marker='X', label='Centers')
plt.legend()
plt.show()自動的にデータの中心を見つけ出し、適切にクラスタリングできていることが視覚的に確認できます。もしここでK-Meansを使う場合は、n_clusters=3と指定する必要がありますが、MeanShiftはその必要がありませんでした。
まとめ:MeanShiftとK-Meansの使い分け
今回は、MeanShiftクラスタリングをK-Meansとの比較を交えて解説しました。
- MeanShift: クラスタ数が不明で、任意の形状を想定する場合に有効。ただし計算コストは高め。
- K-Means: クラスタ数に見当がついており、データが大規模な場合に有効。高速だが球状のクラスタを仮定。
「まずはデータの構造を探索的に調べてみたい」という場面ではMeanShiftを使い、 「大量のデータを高速に、決まった数のグループに分けたい」という場面ではK-Meansを使う、 というように、それぞれのアルゴリズムの特性を理解し、目的に応じて使い分けることが重要です。


コメント