下図のような2次元のサンプルデータについて \(k\)-means による非階層クラスタクラスタリングを実行してみよう.
上の散布図を見ると,直感的には次の 3 つのクラスタに分類できそうである.
\(k\)-means は次のような手順でクラスタリングを行います.
ここでは実際に非階層クラスタリングによって直感どおりの分類ができるかどうか確かめよう.
まず,Anaconda Prompt 等で pip list
を実行して「scikit-learn」パッケージがインストールされていることを確認する.もしもインストールされていなければ pip install scikit-learn
でインストールしておく.Jupyter Notebook のシェルコマンドを利用しても構いません.「scikit-learn」のインストールが確認できれば,必要なモジュールをインポートする.
モジュールのインポートimport pandas as pd
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
次に,GitHub のリポジトリからサンプルデータ (clustering-sample.csv) を Pandas のデータフレームに読み込んで表示してみる.なお,Web ブラウザで CSV ファイルをダウンロードして Python プログラムと同じフォルダにコピーしたものを読み込んでも良い.
サンプルデータの読み込みurl = "https://github.com/rinsaka/sample-data-sets/blob/master/clustering-sample.csv?raw=true"
# url = "clustering-sample.csv" # カレントディレクトリから読み込む場合
df =pd.read_csv(url)
df
上で表示した散布図と同じものを描いてみる.
散布図の作成fig, ax = plt.subplots(1, 1, figsize=(6, 6))
ax.scatter(df['x'], df['y'], alpha=0.5)
ax.set_title("Sample data")
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_xlim(-1, 11)
ax.set_ylim(-1, 11)
# plt.savefig('cluster_sample.png', dpi=300, facecolor='white')
plt.show()
Pandas のデータフレームから x 列と y 列を取り出して NumPy 配列に変換する.
Pandas のデータフレームから NumPy 配列に変換xy = df.loc[:, ['x', 'y']].values
print(xy)
[[ 7.4346 6.652 ] [ 6.5419 6.3611] [ 8.9819 9.2461] [ 3.8554 4.8386] ... (中略) [ 6.6627 7.3856] [ 5.7654 6.0543] [ 2.2411 1.0902]]
クラスタ数を \(k = 3\) に設定し,クラスタリングの計算を実行する.なお,この結果は毎回異なる可能性があるので,再現性を持たせたい場合は 2 行目をコメントアウトし,3 行目を有効にすると良い(詳細はこちら).
クラスタリングの計算を実行k = 3
clf = KMeans(n_clusters=k) # モデルの設定
# clf = KMeans(n_clusters=k, random_state=1) # 再現性を持たせたい場合
clf.fit(xy) # クラスタリングの計算
pred = clf.predict(xy) # 計算結果からサンプルデータがどのクラスタに属するかを予測する
pred
array([1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 0, 1, 2, 2, 1, 2, 0, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 2, 2, 1, 0, 2, 2, 1, 1, 2, 1, 0, 0, 2, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 0, 1, 2, 1, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 2, 2, 0, 2, 1, 2, 2, 1, 0, 0, 1, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 1, 0, 0, 2, 0, 1, 2, 1, 2, 0, 2, 2, 0, 0, 2, 1, 1, 2, 0, 1, 0, 2, 1, 2, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 1, 1, 0, 2, 2, 2, 1, 2, 0, 1, 2, 0, 2, 2, 2, 2, 1, 0, 2, 2, 2, 1, 1, 2, 0, 0, 0, 2, 2, 1, 0, 0, 2, 2, 0, 1, 2, 2, 0, 0, 2, 1, 0, 0, 2, 1, 0, 0, 2, 1, 1, 1, 2, 1, 2, 1, 0, 1, 1, 0, 0, 1, 2, 0, 2, 1, 1, 1, 2, 1, 2, 1, 0, 0, 2, 0, 2, 0, 0, 0, 1, 1, 0, 2, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 0, 2, 1, 1, 1, 0, 0, 2, 2, 2, 1, 1, 2, 2, 2, 0, 2, 0, 1, 1, 0, 1, 2, 0, 2, 1, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 1, 2, 1, 2, 0, 0, 0, 2, 1, 1, 2, 2, 2, 0, 1, 1, 1, 1, 2, 1, 0, 1, 1, 2, 2, 2, 2, 2, 0, 1, 0, 2, 1, 2, 0, 1, 1, 2], dtype=int32)
Pandas のデータフレームに分類結果を追加する.
分類結果を追加するdf['cluster_id'] = pred
df
分類結果の散布図を描いてみる.直感どおりうまく分割されていることがわかる.
散布図の描画fig, ax = plt.subplots(1, 1, figsize=(6, 6))
colors = ['Red', 'Blue', 'Green']
for cls in range(k):
x = df.loc[df['cluster_id'] == cls, 'x']
y = df.loc[df['cluster_id'] == cls, 'y']
ax.scatter(x, y, alpha=0.5, label=f"cluseter {cls}", color=colors[cls])
ax.set_title("Clustering results")
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_xlim(-1, 11)
ax.set_ylim(-1, 11)
ax.legend(loc='upper left')
# plt.savefig('cluster_scatter.png', dpi=300, facecolor='white')
plt.show()
各クラスタに属するサンプル数の分布を調査する.100 個ずつ 3 つのクラスタに分割されていることがわかる.
サンプル数の確認df['cluster_id'].value_counts()
0 100 1 100 2 100 Name: cluster_id, dtype: int64
各クラスタの特徴を確認する.このとき,ID の平均値 148.73 には意味がないことに注意する.クラスタ 0 は x が 8.21,y が 1.99 を中心とする付近のデータである.
クラスタ0df[df['cluster_id']==0].mean()
ID 148.730000 x 8.213697 y 1.991513 cluster_id 0.000000 dtype: float64
クラスタ 1 は x が 6.92,y が 7.85 を中心とする付近のデータである.
クラスタ1df[df['cluster_id']==1].mean()
ID 149.390000 x 6.919016 y 7.845478 cluster_id 1.000000 dtype: float64
クラスタ 2 は x が 2.92,y が 2.95 を中心とする付近のデータである.
クラスタ2df[df['cluster_id']==2].mean()
ID 150.380000 x 2.921705 y 2.946452 cluster_id 2.000000 dtype: float64
新たなデータが得られたときに,それらがどのクラスタに分類されるかテストしてみよう.
まずは,各クラスタの平均値に近いデータを与えてみる.
新たなデータで分析してみるt1 = np.array([[6., 8.], [9., 2.], [3., 3.]])
print(t1)
clf.predict(t1)
[[6. 8.] [9. 2.] [3. 3.]] array([1, 0, 2], dtype=int32)
次に,各クラスタから離れた値やクラスタの境界に近いデータを与えてみる.
少々いやらしいデータではどのようなクラスタになるだろうか?t2 = np.array([[5., 11.], [11., 2.], [1., 6.], [5.8, 5.8], [5.8, 2.8], [5.0, 5.0]])
print(t2)
clf.predict(t2)
[[ 5. 11. ] [11. 2. ] [ 1. 6. ] [ 5.8 5.8] [ 5.8 2.8] [ 5. 5. ]] array([1, 0, 2, 1, 0, 2], dtype=int32)