読み込み中...

Pythonにおけるk-means法を使用したクラスタリング手法12選

クラスタリング 徹底解説 Python
この記事は約45分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●Pythonクラスタリングの基礎

今回は、Pythonを使ったクラスタリングについて深掘りしていきます。

クラスタリングは、大量のデータから意味のあるパターンを見つけ出す技術です。

皆さんも日々の業務で、似たようなデータをグループ化したいと思ったことはありませんか?

○クラスタリングとは?初心者でもわかる解説

クラスタリングは、データ分析の分野で欠かせない手法です。

簡単に言えば、似たようなデータをグループ(クラスタ)にまとめる作業です。

例えば、お客様の購買履歴をグループ化して、似た傾向を持つ顧客層を見つけ出すことができます。

皆さんの中には、「でも、どうやって似ているかを判断するの?」と疑問に思う方もいるでしょう。

クラスタリングでは、データ間の距離や密度などの指標を使って、似ている度合いを数値化します。

そして、その数値を基にグループ分けを行います。

○なぜPythonがクラスタリングに最適なのか

Pythonは、データ分析やクラスタリングに最適なプログラミング言語です。

その理由をいくつか挙げてみましょう。

まず、Pythonには豊富なライブラリがあります。

scikit-learn、NumPy、Pandasなど、データ分析に特化したライブラリが充実しています。

また、Pythonの文法はシンプルで読みやすく、初心者でも比較的簡単に習得できます。

さらに、Pythonは処理速度が速く、大量のデータを扱う際にも効率的です。

また、可視化ツールも充実しているため、クラスタリング結果を直感的に理解しやすいグラフや図で表現できます。

○サンプルコード1:簡単なクラスタリング入門

では、実際にPythonを使ってクラスタリングを体験してみましょう。

まずは、最も基本的なk-means法を使った簡単な例から始めます。

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

# データの生成
np.random.seed(42)
X = np.random.rand(100, 2)

# K-meansクラスタリングの実行
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(X)

# 結果の可視化
plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels_, cmap='viridis')
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], marker='x', s=200, linewidths=3, color='r')
plt.title('K-meansクラスタリング結果')
plt.show()

このコードでは、まず100個の2次元データポイントをランダムに生成しています。

そして、scikit-learnライブラリのKMeansクラスを使って、これらのデータを3つのクラスタに分類します。

実行結果を見てみましょう。散布図上に、3つの異なる色で表されたクラスタが表示されます。

赤い×印は各クラスタの中心(セントロイド)を示しています。

●k-means法/Pythonで最も使われるクラスタリング手法

データサイエンティストの皆さん、クラスタリング手法の中でも特に重要な「k-means法」について深掘りしていきましょう。

k-means法は、その簡便さと効率性から、Pythonを使用したデータ分析で最も頻繁に活用される手法の一つです。

○k-means法の仕組みと特徴

k-means法の基本的な考え方は非常にシンプルです。

データポイントをk個のグループ(クラスタ)に分類し、各クラスタの中心(セントロイド)からの距離が最小になるようにデータを割り当てていきます。

アルゴリズムの流れは次のようになります。

  1. k個のセントロイドをランダムに初期化します
  2. 各データポイントを最も近いセントロイドに割り当てます
  3. 各クラスタのセントロイドを再計算します
  4. 収束するまで2と3を繰り返します

k-means法の特徴として、計算速度が速く、大規模なデータセットにも適用できる点が挙げられます。

また、結果が直感的に解釈しやすいという利点もあります。

ただし、初期値依存性が高く、外れ値に敏感であるという欠点もあります。

また、クラスタ数kを事前に指定する必要があるため、適切なk値の選択が重要になります。

○サンプルコード2:scikit-learnでk-means実装

では、実際にPythonのscikit-learnライブラリを使ってk-means法を実装してみましょう。

今回は、身長と体重のデータを使って、人々をグループ分けする例を考えてみます。

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

# サンプルデータの生成
np.random.seed(42)
X = np.array([[170, 60], [175, 65], [180, 70], [160, 55], [165, 58], 
              [185, 75], [155, 50], [190, 80], [178, 68], [182, 72]])

# KMeansモデルの初期化とフィッティング
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(X)

# 結果の可視化
plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels_, cmap='viridis')
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], 
            marker='x', s=200, linewidths=3, color='r')
plt.xlabel('身長 (cm)')
plt.ylabel('体重 (kg)')
plt.title('K-means クラスタリング結果')
plt.show()

# クラスタの中心座標を表示
print("クラスタの中心座標:")
for i, center in enumerate(kmeans.cluster_centers_):
    print(f"クラスタ {i}: 身長 {center[0]:.2f}cm, 体重 {center[1]:.2f}kg")

このコードでは、10人分の身長と体重のデータを用意し、3つのクラスタに分類しています。

scikit-learnのKMeansクラスを使用することで、簡単にk-means法を実装できます。

実行結果を見てみましょう。散布図上に、3つの異なる色で表されたクラスタが表示されます。

赤い×印は各クラスタの中心(セントロイド)を表しています。

また、各クラスタの中心座標(平均身長と平均体重)も出力されます。

○サンプルコード3:k-meansの結果を可視化

k-means法の結果を理解するには、可視化が非常に重要です。

先ほどのコードを少し拡張して、より詳細な可視化を行ってみましょう。

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

# サンプルデータの生成(前回と同じ)
np.random.seed(42)
X = np.array([[170, 60], [175, 65], [180, 70], [160, 55], [165, 58], 
              [185, 75], [155, 50], [190, 80], [178, 68], [182, 72]])

# KMeansモデルの初期化とフィッティング
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(X)

# カラーマップの設定
colors = ['#FF9999', '#66B2FF', '#99FF99']
cmap = ListedColormap(colors)

# 結果の可視化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels_, cmap=cmap, s=100)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], 
            marker='x', s=200, linewidths=3, color='r')

# データポイントにラベルを付ける
for i, (x, y) in enumerate(X):
    plt.annotate(f'Person {i+1}', (x, y), xytext=(5, 5), 
                 textcoords='offset points')

plt.xlabel('身長 (cm)')
plt.ylabel('体重 (kg)')
plt.title('K-means クラスタリング結果の詳細可視化')

# 凡例を追加
legend1 = plt.legend(*scatter.legend_elements(),
                    loc="lower right", title="クラスタ")
plt.add_artist(legend1)

# クラスタの中心座標を表示
for i, center in enumerate(kmeans.cluster_centers_):
    plt.annotate(f'Cluster {i+1}\n({center[0]:.0f}cm, {center[1]:.0f}kg)', 
                 (center[0], center[1]), xytext=(10, 10),
                 textcoords='offset points', fontweight='bold')

plt.grid(True)
plt.show()

この拡張版では、各データポイントに番号を付け、クラスタの中心座標も図中に表示しています。

また、色分けをより見やすくし、グリッド線も追加しました。

実行結果を見ると、データポイントがどのようにグループ化されているか、より詳細に理解できます。

例えば、身長が高く体重も重い人々が一つのクラスタを形成し、逆に身長が低く体重も軽い人々が別のクラスタを形成していることが一目で分かります。

k-means法を用いたクラスタリングと可視化について、理解が深まったでしょうか?こ

の手法は単純ですが非常に強力で、多くの実際のデータ分析シナリオで活用できます。

例えば、顧客セグメンテーション、画像の色削減、異常検知など、様々な分野で応用が可能です。

●Pythonで使える高度なクラスタリングアルゴリズム

Pythonには、様々な状況に対応できる多彩なクラスタリング手法が用意されています。

今回は、階層的クラスタリングとDBSCAN(密度ベースクラスタリング)という二つの強力なアルゴリズムを詳しく見ていきます。

○階層的クラスタリングの魅力

階層的クラスタリングは、データポイント間の距離に基づいてクラスタを形成していく手法です。

k-means法とは異なり、クラスタ数を事前に指定する必要がありません。

代わりに、データの階層構造を表す樹形図(デンドログラム)を生成します。

階層的クラスタリングには、ボトムアップ方式(凝集型)とトップダウン方式(分割型)がありますが、一般的にはボトムアップ方式が使用されます。

この方式では、まず各データポイントを個別のクラスタとみなし、最も近い2つのクラスタを順次結合していきます。

階層的クラスタリングの魅力は、データの構造を視覚的に理解できる点にあります。

デンドログラムを見ることで、クラスタがどのように形成されていくかを直感的に把握できます。

また、任意の段階でクラスタリングを「切る」ことで、異なる粒度のクラスタリング結果を得ることができます。

○サンプルコード4:scipy使用したウォード法の実装

それでは、実際にPythonを使って階層的クラスタリングを実装してみましょう。

今回は、scipyライブラリを使用し、ウォード法という手法を適用します。

ウォード法は、クラスタ内の分散を最小化する方法で、多くの場合で良好な結果を得られます。

import numpy as np
from scipy.cluster.hierarchy import dendrogram, linkage
from matplotlib import pyplot as plt

# サンプルデータの生成
np.random.seed(42)
X = np.random.rand(20, 2)

# 階層的クラスタリングの実行(ウォード法)
Z = linkage(X, 'ward')

# デンドログラムの描画
plt.figure(figsize=(10, 7))
dendrogram(Z)
plt.title('階層的クラスタリング(ウォード法)のデンドログラム')
plt.xlabel('サンプル番号')
plt.ylabel('距離')
plt.show()

# クラスタ数を3に設定した場合の結果表示
from scipy.cluster.hierarchy import fcluster

cluster_labels = fcluster(Z, t=3, criterion='maxclust')

plt.figure(figsize=(10, 7))
plt.scatter(X[:, 0], X[:, 1], c=cluster_labels, cmap='viridis')
plt.title('階層的クラスタリング結果(クラスタ数: 3)')
plt.xlabel('X')
plt.ylabel('Y')
plt.colorbar(label='クラスタ番号')
plt.show()

このコードでは、まず20個の2次元データポイントをランダムに生成します。

次に、scipyのlinkage関数を使ってクラスタリングを実行し、その結果をデンドログラムとして可視化します。

さらに、クラスタ数を3に設定した場合の結果も散布図で表示しています。

実行結果を見てみましょう。

デンドログラムでは、データポイントがどのように結合されていくかが階層的に表現されています。

縦軸は距離を表し、横軸はサンプル番号を表しています。

また、散布図では、3つのクラスタがそれぞれ異なる色で表示されています。

階層的クラスタリングの面白い点は、クラスタ数を変更するだけで異なる粒度の結果が得られることです。

例えば、クラスタ数を2や4に変更すると、データの分類がどのように変化するか観察できます。

○密度ベースクラスタリング

次に、DBSCANというアルゴリズムを見ていきましょう。

DBSCANは「Density-Based Spatial Clustering of Applications with Noise」の略で、密度ベースのクラスタリング手法です。

k-means法や階層的クラスタリングとは異なり、クラスタの形状が非球形でも効果的に検出できるという特徴があります。

DBSCANの基本的な考え方は、データポイントの密度が高い領域をクラスタとみなすというものです。

具体的には、あるポイントの周りに一定数以上の近傍点が存在する場合、そのポイントをクラスタの「コア」とみなします。

コアポイント同士が密接に連結している領域がクラスタとして検出されます。

DBSCANの大きな利点は、ノイズ(外れ値)を自動的に検出できる点です。

また、クラスタ数を事前に指定する必要がないため、データの本質的な構造を見出すのに適しています。

○サンプルコード5:scikit-learnでDBSCAN実装

それでは、scikit-learnライブラリを使ってDBSCANを実装してみましょう。

今回は、やや複雑な形状のデータセットを用意し、DBSCANの性能を確認します。

import numpy as np
from sklearn.cluster import DBSCAN
from sklearn.datasets import make_moons
import matplotlib.pyplot as plt

# 半月型のデータセットを生成
X, _ = make_moons(n_samples=200, noise=0.05, random_state=42)

# DBSCANの実行
dbscan = DBSCAN(eps=0.3, min_samples=5)
cluster_labels = dbscan.fit_predict(X)

# 結果の可視化
plt.figure(figsize=(10, 7))
scatter = plt.scatter(X[:, 0], X[:, 1], c=cluster_labels, cmap='viridis')
plt.title('DBSCANクラスタリング結果')
plt.xlabel('X')
plt.ylabel('Y')
plt.colorbar(scatter, label='クラスタラベル')
plt.show()

# クラスタ数とノイズポイントの数を表示
n_clusters = len(set(cluster_labels)) - (1 if -1 in cluster_labels else 0)
n_noise = list(cluster_labels).count(-1)
print(f'推定されたクラスタ数: {n_clusters}')
print(f'ノイズとして分類されたポイント数: {n_noise}')

このコードでは、scikit-learnのmake_moons関数を使って半月型のデータセットを生成しています。

DBSCANアルゴリズムを適用し、結果を散布図で可視化しています。

実行結果を見てみましょう。半月型の2つのクラスタが正確に検出されていることがわかります。

また、ノイズポイント(クラスタに属さないポイント)も自動的に識別されています。

コンソール出力では、検出されたクラスタ数とノイズポイントの数が表示されます。

DBSCANの面白い特徴は、パラメータ(eps、min_samples)を調整することで、クラスタリングの粒度を変更できる点です。

epsを大きくすると、より大きなクラスタが形成され、小さくすると、より細かいクラスタに分割されます。

●多次元データのクラスタリングと可視化テクニック

データサイエンティストの皆さん、実世界のデータ分析では、2次元や3次元を超える多次元データを扱うことがよくあります。

例えば、顧客の購買履歴、センサーデータ、遺伝子発現データなど、多くの特徴量を持つデータセットに遭遇することでしょう。

しかし、人間の脳は3次元以上の空間を直感的に理解することが苦手です。

そこで、多次元データを効果的に分析し可視化する技術が重要になってきます。

今回は、多次元データのクラスタリングと可視化に焦点を当て、特に強力な次元削減手法であるPCA(主成分分析)とt-SNEについて詳しく見ていきましょう。

○PCAとt-SNEを使った次元削減の方法

PCA(Principal Component Analysis)は、データの分散を最大限保持しながら、高次元のデータを低次元に圧縮する手法です。

PCAは線形変換を用いるため、計算が比較的高速で、大規模なデータセットにも適用できます。

一方、t-SNE(t-distributed Stochastic Neighbor Embedding)は、局所的な構造を保持しながら高次元データを2次元または3次元に埋め込む非線形手法です。

t-SNEは特に、クラスタ構造の可視化に優れており、複雑なデータセットの探索的分析に適しています。

PCAとt-SNEは、それぞれ異なる特性を持っているため、用途に応じて使い分けることが重要です。

PCAは全体的な構造を把握するのに適していますが、局所的な構造を見落とす可能性があります。

対照的に、t-SNEは局所的な構造を詳細に表現できますが、グローバルな構造を歪める可能性があります。

○サンプルコード6:PCAを用いた可視化

それでは、実際にPythonを使ってPCAを実装し、多次元データの可視化を行ってみましょう。

今回は、scikit-learnのIris(アヤメ)データセットを使用します。

このデータセットは4つの特徴量を持つため、PCAを用いて2次元に圧縮し、可視化します。

from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import numpy as np

# Irisデータセットの読み込み
iris = load_iris()
X = iris.data
y = iris.target

# PCAの実行
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)

# 結果の可視化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='viridis')
plt.title('PCAによるIrisデータセットの2次元可視化')
plt.xlabel('第1主成分')
plt.ylabel('第2主成分')
plt.colorbar(scatter, label='品種')

# 主成分の寄与率を表示
print("各主成分の寄与率:")
print(pca.explained_variance_ratio_)

plt.show()

このコードでは、まずscikit-learnからIrisデータセットを読み込みます。

PCAクラスを使用して4次元のデータを2次元に圧縮し、結果を散布図で可視化しています。

また、各主成分の寄与率(説明分散比)も出力しています。

実行結果を見てみましょう。

散布図では、3つの異なるアヤメの品種がそれぞれ異なる色で表示されています。

第1主成分と第2主成分を軸とした2次元平面上で、データ点がどのように分布しているかが一目でわかります。

また、コンソール出力では各主成分がデータの分散をどの程度説明しているかが表示されます。

PCAの興味深い点は、元のデータの特徴量がどのように新しい主成分に寄与しているかを分析できることです。

例えば、第1主成分にどの元の特徴量が大きく影響しているかを調べることで、データの構造についての洞察を得ることができます。

○サンプルコード7:t-SNEによる高度な可視化

続いて、t-SNEを使った可視化を行ってみましょう。

t-SNEは特に高次元データの非線形構造を捉えるのに優れているため、より複雑なデータセットに適用してみます。

今回は、手書き数字のデータセットであるMNISTを使用します。

from sklearn.datasets import load_digits
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import numpy as np

# MNISTデータセット(簡易版)の読み込み
digits = load_digits()
X = digits.data
y = digits.target

# t-SNEの実行
tsne = TSNE(n_components=2, random_state=42)
X_tsne = tsne.fit_transform(X)

# 結果の可視化
plt.figure(figsize=(12, 8))
scatter = plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='tab10')
plt.title('t-SNEによるMNISTデータセットの2次元可視化')
plt.colorbar(scatter, label='数字')

# ランダムに選んだ点にラベルを付ける
for i in range(10):
    idx = np.where(y == i)[0]
    idx = np.random.choice(idx)
    plt.annotate(str(y[idx]), (X_tsne[idx, 0], X_tsne[idx, 1]), xytext=(5, 5), 
                 textcoords='offset points', fontsize=12, fontweight='bold')

plt.show()

このコードでは、scikit-learnのload_digits関数を使ってMNISTデータセット(簡易版)を読み込みます。

t-SNEを適用して64次元のデータを2次元に圧縮し、結果を散布図で可視化しています。

また、各クラスタにラベルを付けて、どの数字がどこに位置しているかを表しています。

実行結果を見てみましょう。

異なる色で表示された10個のクラスタが形成されていることがわかります。

各クラスタは、0から9までの数字に対応しています。

t-SNEは局所的な構造を保持するため、似た数字(例えば3と5、1と7)が近くに配置される傾向があります。

t-SNEの面白い特徴は、異なる実行で異なる結果が得られる点です。

乱数シードを固定しても、初期化や最適化プロセスの違いにより、結果が少し異なることがあります。

ただし、全体的なクラスタ構造は通常保持されます。

多次元データの可視化は、データの隠れたパターンや構造を発見するための強力なツールです。

PCAとt-SNEを使いこなすことで、複雑なデータセットから意味のある洞察を得ることができます。

ただし、どちらの手法も情報の損失を伴うため、結果の解釈には注意が必要です。

●クラスタリングの評価と最適化/より良い結果を得るために

データサイエンティストの皆さん、クラスタリングアルゴリズムを実装し、結果を可視化できるようになりましたね。

しかし、ここで重要な疑問が生じます。「得られたクラスタリング結果は本当に良いものなのだろうか?」「もっと最適な結果があるのではないか?」と。

クラスタリングの評価と最適化は、データ分析プロセスにおいて極めて重要な段階です。

適切な評価と最適化を行うことで、より信頼性の高い、意味のある結果を得ることができます。

今回は、クラスタリングの評価と最適化に焦点を当て、特に重要な二つの手法、エルボー法とシルエット分析について詳しく見ていきましょう。

○エルボー法でクラスタ数を決定しよう

エルボー法は、k-means法などのクラスタリングアルゴリズムで最適なクラスタ数を決定するための一般的な方法です。

この手法は、クラスタ数を増やしていくと、クラスタ内の分散(または誤差)が減少していく様子をグラフ化し、「肘」のように曲がる点を最適なクラスタ数と判断します。

エルボー法の基本的な考え方は次のとおりです。

  1. クラスタ数kを1から順に増やしていきます
  2. 各kに対して、クラスタリングを実行し、クラスタ内分散の合計(または平均二乗誤差)を計算します
  3. kとクラスタ内分散の関係をグラフにプロットします
  4. グラフが急激に曲がる「肘」の位置を見つけ、そのkを最適なクラスタ数とします

○サンプルコード8:エルボー法の実装と解釈

それでは、Pythonを使ってエルボー法を実装してみましょう。

scikit-learnのmake_blobs関数を使って人工的なデータセットを生成し、エルボー法でクラスタ数を決定します。

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

# データセットの生成
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)

# エルボー法の実装
wcss = []
for i in range(1, 11):
    kmeans = KMeans(n_clusters=i, init='k-means++', max_iter=300, n_init=10, random_state=0)
    kmeans.fit(X)
    wcss.append(kmeans.inertia_)

# エルボーカーブのプロット
plt.figure(figsize=(10, 8))
plt.plot(range(1, 11), wcss, marker='o')
plt.title('エルボー法によるクラスタ数の決定')
plt.xlabel('クラスタ数')
plt.ylabel('WCSS (Within-Cluster Sum of Squares)')
plt.show()

# 最適なクラスタ数の決定(簡易的な方法)
differences = np.diff(wcss)
elbow_point = np.argmin(differences) + 1
print(f"エルボー法により推定された最適なクラスタ数: {elbow_point + 1}")

このコードでは、まず300個のデータポイントを持つ人工的なデータセットを生成します。

そして、クラスタ数を1から10まで変化させながらk-means法を適用し、各クラスタ数に対するWCSS(Within-Cluster Sum of Squares)を計算します。

最後に、クラスタ数とWCSSの関係をグラフにプロットします。

実行結果を見てみましょう。

グラフ上で「肘」のように曲がっている点が最適なクラスタ数を示唆しています。

また、簡易的な方法として、WCSSの差分が最小になる点を自動的に検出し、最適なクラスタ数を推定しています。

エルボー法の解釈には注意が必要です。

明確な「肘」が見られない場合もあり、そのような場合は他の評価指標と組み合わせて判断する必要があります。

○シルエット分析/クラスタの品質を評価する

シルエット分析は、クラスタリング結果の品質を評価するための強力な手法です。

各データポイントについて、同じクラスタ内の他のポイントとの類似度と、最も近い他のクラスタのポイントとの類似度を比較します。

シルエットスコアは-1から1の範囲をとり、1に近いほど良いクラスタリング結果を示します。

シルエット分析の基本的な考え方は次のとおりです。

  1. 各データポイントについて、同じクラスタ内の他のポイントとの平均距離(a)を計算します。
  2. 各データポイントについて、最も近い他のクラスタのポイントとの平均距離(b)を計算します。
  3. シルエットスコア s = (b – a) / max(a, b) を計算します。
  4. すべてのデータポイントの平均シルエットスコアを求めます。

○サンプルコード9:シルエットスコアの計算と可視化

それでは、Pythonを使ってシルエット分析を実装してみましょう。

先ほどのデータセットを使って、k-means法の結果に対してシルエット分析を行います。

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import numpy as np

# 先ほどのデータセットを使用
# X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)

# 最適なクラスタ数でK-meansを実行
n_clusters = 4  # エルボー法で決定したクラスタ数
kmeans = KMeans(n_clusters=n_clusters, random_state=0)
cluster_labels = kmeans.fit_predict(X)

# シルエットスコアの計算
silhouette_avg = silhouette_score(X, cluster_labels)
sample_silhouette_values = silhouette_samples(X, cluster_labels)

print(f"全体の平均シルエットスコア: {silhouette_avg:.3f}")

# シルエットプロットの作成
plt.figure(figsize=(12, 8))
y_lower = 10
for i in range(n_clusters):
    ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
    ith_cluster_silhouette_values.sort()

    size_cluster_i = ith_cluster_silhouette_values.shape[0]
    y_upper = y_lower + size_cluster_i

    color = plt.cm.nipy_spectral(float(i) / n_clusters)
    plt.fill_betweenx(np.arange(y_lower, y_upper),
                      0, ith_cluster_silhouette_values,
                      facecolor=color, edgecolor=color, alpha=0.7)

    plt.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
    y_lower = y_upper + 10

plt.title("シルエット分析の結果")
plt.xlabel("シルエットスコア")
plt.ylabel("クラスタラベル")
plt.axvline(x=silhouette_avg, color="red", linestyle="--")
plt.yticks([])  # y軸のラベルを消す
plt.show()

# クラスタリング結果の散布図
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X[:, 0], X[:, 1], c=cluster_labels, cmap='viridis')
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='red', marker='x', s=200, linewidths=3)
plt.title("K-meansクラスタリング結果")
plt.colorbar(scatter, label='クラスタラベル')
plt.show()

このコードでは、まずk-means法でクラスタリングを行い、その結果に対してシルエット分析を適用します。

全体の平均シルエットスコアを計算し、各データポイントのシルエットスコアを可視化します。

また、クラスタリング結果の散布図も合わせて表示します。

実行結果を見てみましょう。

シルエットプロットでは、各クラスタのシルエットスコアの分布が表示されています。

幅の広いクラスタほど多くのデータポイントを含んでいます。

平均シルエットスコアが赤い点線で示されており、この値が高いほど良好なクラスタリング結果を表します。

散布図では、異なる色で表示された4つのクラスタと、赤い×印で示されたクラスタの中心が確認できます。

シルエット分析の結果を解釈する際は、全体の平均スコアだけでなく、各クラスタのスコア分布も考慮することが重要です。

均一で高いスコアを持つクラスタが理想的ですが、現実のデータではばらつきが生じることも珍しくありません。

クラスタリングの評価と最適化は、データ分析プロセスにおいて非常に重要な段階です。

エルボー法やシルエット分析などの手法を適切に活用することで、より信頼性の高い、意味のあるクラスタリング結果を得ることができます。ただし、これらの手法も万能ではありません。

データの特性や分析の目的に応じて、複数の評価指標を組み合わせたり、ドメイン知識を活用したりすることが重要です。

●よくあるエラーと対処法・トラブルシューティング

クラスタリングアルゴリズムの基本から評価方法まで学んできましたね。

しかし、実際のプロジェクトでクラスタリングを適用する際には、様々な問題に直面することがあります。

「エラーが出て実行できない」「結果が安定しない」といった悩みを抱えたことはありませんか?

今回は、Pythonでクラスタリングを行う際によく遭遇するエラーや問題点、そしてその対処法について詳しく見ていきましょう。

○「メモリエラー」の解決策

大規模なデータセットでクラスタリングを行う際、しばしば「メモリエラー」に遭遇します。

特にk-means法やDBSCAN等のアルゴリズムは、データ量に応じてメモリ使用量が急増する傾向があります。

メモリエラーを解決するためのアプローチをいくつか紹介します。

□データのサブサンプリング

全データの一部をランダムに抽出してクラスタリングを行います。

import numpy as np
from sklearn.cluster import KMeans

# 大規模なデータセットを想定
X = np.random.rand(1000000, 10)

# サブサンプリング
sample_size = 100000
indices = np.random.choice(X.shape[0], sample_size, replace=False)
X_sampled = X[indices]

# サブサンプルでクラスタリング
kmeans = KMeans(n_clusters=5)
kmeans.fit(X_sampled)

□ミニバッチK-means

通常のk-means法の代わりに、ミニバッチ版を使用します。

メモリ効率が良く、大規模データセットに適しています。

from sklearn.cluster import MiniBatchKMeans

# ミニバッチK-meansの実行
mbkmeans = MiniBatchKMeans(n_clusters=5, batch_size=1000)
mbkmeans.fit(X)

□インクリメンタル学習

データを小さなバッチに分割し、逐次的に学習を行います。

from sklearn.cluster import MiniBatchKMeans
import numpy as np

# 大規模データセットをシミュレート
X = np.random.rand(1000000, 10)

# インクリメンタル学習の設定
mbkmeans = MiniBatchKMeans(n_clusters=5, batch_size=1000)

# バッチサイズ
batch_size = 10000

# インクリメンタル学習の実行
for i in range(0, X.shape[0], batch_size):
    X_batch = X[i:i+batch_size]
    mbkmeans.partial_fit(X_batch)

print("学習完了")

この方法を適用することで、メモリエラーを回避しつつ大規模データセットのクラスタリングが可能になります。

○「収束しない」問題への対処

k-means法などの反復アルゴリズムでは、時に「収束しない」という問題に直面します。

収束しない主な原因として、不適切な初期値設定や、データの特性が挙げられます。

収束問題に対処するためのテクニックをいくつか紹介します。

□最大反復回数の調整

デフォルトの反復回数を増やすことで、収束する可能性が高まります。

from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=5, max_iter=1000)  # max_iterを大きく設定
kmeans.fit(X)

□初期化方法の変更

k-means++法を使用することで、より良い初期クラスタ中心を選択できます。

kmeans = KMeans(n_clusters=5, init='k-means++')
kmeans.fit(X)

□複数回の実行

異なる初期値で複数回実行し、最も良い結果を選択します。

best_kmeans = None
best_inertia = float('inf')

for _ in range(10):  # 10回試行
    kmeans = KMeans(n_clusters=5)
    kmeans.fit(X)
    if kmeans.inertia_ < best_inertia:
        best_inertia = kmeans.inertia_
        best_kmeans = kmeans

print("最良の結果を選択しました")

この方法を組み合わせることで、収束の問題を緩和し、より安定したクラスタリング結果を得ることができます。

○初期値依存性を克服する方法

k-means法などのアルゴリズムは、初期値に大きく依存する傾向があります。

異なる初期値で実行すると、全く異なる結果が得られることがあります。

初期値依存性を克服するためのアプローチをいくつか紹介します。

□k-means++法の使用

先ほども触れましたが、k-means++法を使用することで、より安定した結果が得られます。

kmeans = KMeans(n_clusters=5, init='k-means++', n_init=10)
kmeans.fit(X)

□アンサンブル手法

複数回のクラスタリング結果を組み合わせて、より安定した結果を得ます。

import numpy as np
from sklearn.cluster import KMeans
from scipy.stats import mode

def ensemble_kmeans(X, n_clusters, n_runs=10):
    labels_list = []
    for _ in range(n_runs):
        kmeans = KMeans(n_clusters=n_clusters)
        labels = kmeans.fit_predict(X)
        labels_list.append(labels)

    # 最頻値を取ることでアンサンブル
    ensemble_labels = mode(labels_list, axis=0)[0].ravel()
    return ensemble_labels

# アンサンブルK-meansの実行
ensemble_labels = ensemble_kmeans(X, n_clusters=5)
print("アンサンブルクラスタリング完了")

□階層的クラスタリングとの組み合わせ

階層的クラスタリングの結果を初期値として使用することで、より安定した結果が得られることがあります。

from sklearn.cluster import AgglomerativeClustering, KMeans

# 階層的クラスタリングの実行
hc = AgglomerativeClustering(n_clusters=5)
hc_labels = hc.fit_predict(X)

# 階層的クラスタリングの結果を初期値として使用
kmeans = KMeans(n_clusters=5, init=hc_labels, n_init=1)
kmeans.fit(X)

print("階層的クラスタリングとK-meansの組み合わせ完了")

この方法を適切に使用することで、初期値依存性の問題を軽減し、より信頼性の高いクラスタリング結果を得ることができます。

クラスタリングにおけるエラーや問題は、データの特性や分析の目的によって様々です。

ここで紹介した方法は、多くの一般的な問題に対処できますが、常に自分のデータや目的に合わせて適切な手法を選択することが重要です。

●Pythonクラスタリングの実践的応用例

データサイエンティストの皆さん、ここまでクラスタリングの基礎から応用、そして問題解決までを学んできました。

理論は理解できたものの、「実際のプロジェクトでどのように活用すればいいのか」と悩んでいる方も多いのではないでしょうか。

ここでは、Pythonを使ったクラスタリングの実践的な応用例を紹介します。

テキストデータ、時系列データ、画像データという3つの異なるタイプのデータに対するクラスタリングの適用方法を具体的に見ていきましょう。

○サンプルコード10:テキストデータのクラスタリング

テキストデータのクラスタリングは、文書分類や話題抽出などに広く利用されています。

ここでは、ニュース記事のテキストデータを使って、記事をトピックごとにクラスタリングする例を紹介します。

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

# サンプルのニュース記事データ
news_articles = [
    "The stock market saw significant gains today.",
    "A new study reveals the health benefits of regular exercise.",
    "Tech company launches innovative smartphone with AI capabilities.",
    "Scientists discover a new species in the Amazon rainforest.",
    "Global efforts to combat climate change are intensifying.",
    "Economists predict a strong recovery for the housing market.",
    "Breakthrough in renewable energy technology announced.",
    "Sports team wins championship after an exciting final match.",
    "Medical researchers make progress in cancer treatment.",
    "Political tensions rise as countries disagree on trade policies."
]

# TF-IDFベクトル化
vectorizer = TfidfVectorizer(stop_words='english')
X = vectorizer.fit_transform(news_articles)

# K-meansクラスタリングの実行
n_clusters = 3
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
kmeans.fit(X)

# 結果の可視化(2次元に削減して表示)
from sklearn.decomposition import TruncatedSVD

svd = TruncatedSVD(n_components=2)
X_2d = svd.fit_transform(X)

plt.figure(figsize=(12, 8))
scatter = plt.scatter(X_2d[:, 0], X_2d[:, 1], c=kmeans.labels_, cmap='viridis')
plt.title('ニュース記事のクラスタリング結果')
plt.colorbar(scatter, label='クラスタ')

for i, txt in enumerate(news_articles):
    plt.annotate(f'Article {i+1}', (X_2d[i, 0], X_2d[i, 1]), xytext=(5, 5), 
                 textcoords='offset points', fontsize=8)

plt.show()

# 各クラスタの代表的な単語を表示
print("各クラスタの代表的な単語:")
order_centroids = kmeans.cluster_centers_.argsort()[:, ::-1]
terms = vectorizer.get_feature_names_out()

for i in range(n_clusters):
    print(f"Cluster {i}:")
    for ind in order_centroids[i, :10]:
        print(f" {terms[ind]}", end='')
    print()

このコードでは、まずTF-IDF(Term Frequency-Inverse Document Frequency)を使ってテキストデータを数値ベクトルに変換します。

その後、K-means法を適用してクラスタリングを行います。

結果は2次元に削減して可視化し、各クラスタの代表的な単語も表示します。

実行結果を見ると、ニュース記事が3つのクラスタに分類されていることがわかります。

散布図では、各点が一つの記事を表し、色分けされたクラスタが確認できます。

また、各クラスタの代表的な単語リストから、そのクラスタのトピックを推測することができます。

○サンプルコード11:時系列データへのクラスタリング適用

時系列データのクラスタリングは、株価の分析や異常検知など、様々な分野で活用されています。

ここでは、複数の株価の時系列データをクラスタリングする例を示します。

import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from tslearn.preprocessing import TimeSeriesScalerMeanVariance
from tslearn.clustering import TimeSeriesKMeans
import matplotlib.pyplot as plt

# 人工的な株価データの生成
np.random.seed(42)
n_series = 50
n_points = 100

def generate_stock_data(trend, volatility):
    return np.cumsum(trend + np.random.normal(0, volatility, n_points))

stock_data = np.array([generate_stock_data(np.random.uniform(-0.1, 0.1), np.random.uniform(0.5, 1.5)) 
                       for _ in range(n_series)])

# データの正規化
scaler = TimeSeriesScalerMeanVariance(mu=0., std=1.)
stock_data_scaled = scaler.fit_transform(stock_data)

# 時系列K-meansクラスタリングの実行
n_clusters = 3
tskm = TimeSeriesKMeans(n_clusters=n_clusters, metric="dtw", random_state=42)
labels = tskm.fit_predict(stock_data_scaled)

# 結果の可視化
plt.figure(figsize=(15, 10))
for i in range(n_clusters):
    cluster = stock_data_scaled[labels == i]
    for series in cluster:
        plt.plot(series.ravel(), alpha=0.4, color=f'C{i}')
    plt.plot(tskm.cluster_centers_[i].ravel(), linewidth=2, color=f'C{i}', label=f'Cluster {i} Center')

plt.title('株価時系列データのクラスタリング結果')
plt.legend()
plt.show()

# クラスタごとの株価の特徴を分析
for i in range(n_clusters):
    cluster = stock_data[labels == i]
    mean_return = np.mean([np.mean(np.diff(series)) for series in cluster])
    volatility = np.mean([np.std(np.diff(series)) for series in cluster])
    print(f"Cluster {i}:")
    print(f"  平均リターン: {mean_return:.4f}")
    print(f"  平均ボラティリティ: {volatility:.4f}")

このコードでは、まず人工的な株価データを生成し、TimeSeriesKMeansを使ってクラスタリングを行います。

DTW(Dynamic Time Warping)距離を使用することで、時間軸のずれを考慮したクラスタリングが可能になります。

結果は、各クラスタの時系列データと中心を可視化し、クラスタごとの特徴(平均リターンとボラティリティ)を分析します。

実行結果を見ると、似たような動きをする株価がグループ化されていることがわかります。

各色が異なるクラスタを表し、太い線がそのクラスタの中心を表しています。

また、クラスタごとの特徴分析から、リスクとリターンの異なる株価グループを識別できます。

○サンプルコード12:画像セグメンテーションにクラスタリングを使う

画像セグメンテーションは、画像を意味のある領域に分割する処理で、コンピュータビジョンの重要なタスクの一つです。

ここでは、K-means法を使って簡単な画像セグメンテーションを行う例を紹介します。

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

# 画像の読み込み
image = io.imread('path_to_your_image.jpg')  # 適切な画像パスに変更してください
image = image / 255.0  # 正規化

# 画像データの整形
h, w, d = image.shape
image_array = np.reshape(image, (h * w, d))

# K-meansクラスタリングの実行
n_clusters = 5
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
labels = kmeans.fit_predict(image_array)

# セグメンテーション結果の生成
segmented_image = kmeans.cluster_centers_[labels]
segmented_image = np.reshape(segmented_image, (h, w, d))

# 結果の可視化
fig, axes = plt.subplots(1, 2, figsize=(15, 7))
axes[0].imshow(image)
axes[0].set_title('Original Image')
axes[0].axis('off')
axes[1].imshow(segmented_image)
axes[1].set_title('Segmented Image')
axes[1].axis('off')
plt.show()

# 各セグメントの色情報を表示
print("各セグメントの代表色 (RGB):")
for i, color in enumerate(kmeans.cluster_centers_):
    print(f"Segment {i}: R={color[0]:.2f}, G={color[1]:.2f}, B={color[2]:.2f}")

このコードでは、画像の各ピクセルをRGB値を持つデータポイントとして扱い、K-means法でクラスタリングします。

各クラスタは画像の一つのセグメントに対応し、そのクラスタの中心がそのセグメントの代表色となります。

実行結果を見ると、元の画像が5つの主要な色領域に分割されていることがわかります。

右側のセグメンテーション後の画像では、類似した色のピクセルが同じセグメントにグループ化されています。

また、各セグメントの代表色情報も出力されるため、画像の主要な色構成を把握することができます。

まとめ

ここまでの学習を通じて、皆さんはPythonを使ったクラスタリングの基礎から応用まで、幅広い知識とスキルを解説してきました。

しかし、新しいアルゴリズムや技術が日々生まれており、学習の終わりはありません。

今後は、ここで学んだ内容を実際のプロジェクトに適用してみることをお勧めします。

理論と実践を組み合わせることで、より深い理解と経験を得ることができると思います。