
書記が数学やるだけ#576 k-平均法
非階層型クラスタリングの代表例であるk-平均法について見ていく。
問題

説明
k平均法は,逐次的なアルゴリズムによりクラスターに分割していく。


クラスター数の設定には特に決まりはないが,エルボー法やシルエット分析が参考になる。

解答
初期配置を決めたら,「各クラスターの重心を求める」→「最も近い重心にクラスターを割り当てる」を繰り返せば良い。

では実際のデータで見てみる。
#必要なライブラリ
from sklearn import datasets
from sklearn.decomposition import PCA
import numpy as np
import pandas as pd
from pandas import DataFrame
import matplotlib.pyplot as plt
import seaborn as sns
#irisデータセット
dataset = datasets.load_iris()
target_names = dataset.target_names
targets = dataset.target
feature_names = dataset.feature_names
features = dataset.data
df = DataFrame(features, columns = feature_names)
df['target'] = target_names[targets]
#"petal length (cm)", “sepal length (cm)”の2変数
df1 = df[["petal length (cm)", "petal width (cm)", "target"]]#実際のデータセット
sns.scatterplot(x='petal length (cm)', y='petal width (cm)', hue='target', data=df1)
まずは標準化をしておく。
df2 = df[["petal length (cm)", "petal width (cm)"]]
#標準化
from sklearn.preprocessing import StandardScaler
dfSS = DataFrame(StandardScaler().fit_transform(df2))
#可視化
sns.scatterplot(x=0, y=1, data=dfSS)
k-平均法を実行,まずは実際のデータ通りクラスター数3でやってみる。
#k-平均法の実行
from sklearn.cluster import KMeans
model = KMeans(n_clusters=3, random_state=0)
model.fit(dfSS)
clusters = model.predict(dfSS)
clustersarray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
各クラスター重心の座標も得られている。
#クラスター重心の座標
df_cluster_centers = pd.DataFrame(model.cluster_centers_)
df_cluster_centers.columns = ['x1', 'x2']
df_cluster_centers
以下で結果の可視化をしておく。
df = pd.DataFrame(dfSS)
df['class'] = clusters
# クラスタリングデータ
sns.scatterplot(data=df, x=0, y=1, hue='class', palette='bright')
# クラスター重心
sns.scatterplot(data=df_cluster_centers, x='x1', y='x2', s=400, marker='*', color='black')
plt.show()
それでは,検証としてエルボー法やシルエット分析を行ってみる。
エルボー分析からは,4以降は減少が緩やかななのでこれ以上増やす必要はなく,2か3が望ましいと考えられる。
# エルボー法を実行
sum_of_squared_errors = []
for i in range(1, 11):
model = KMeans(n_clusters=i, random_state=0, init='random')
model.fit(dfSS)
sum_of_squared_errors.append(model.inertia_) # 損失関数の値を保存
plt.plot(range(1, 11), sum_of_squared_errors, marker='o')
plt.xlabel('number of clusters')
plt.ylabel('sum of squared errors')
plt.show()
シルエット分析からは,4以降ではシルエット係数が不均一であったり厚さも揃っておらず,不適当であると考えられる。2と3のどちらが優れているかは微妙なラインだと思う。
#シルエット分析
import matplotlib.cm as cm
from sklearn.metrics import silhouette_samples
def show_silhouette(fitted_model):
cluster_labels = np.unique(fitted_model.labels_)
num_clusters = cluster_labels.shape[0]
silhouette_vals = silhouette_samples(dfSS, fitted_model.labels_) # シルエット係数の計算
# 可視化
y_ax_lower, y_ax_upper = 0, 0
y_ticks = []
for idx, cls in enumerate(cluster_labels):
cls_silhouette_vals = silhouette_vals[fitted_model.labels_==cls]
cls_silhouette_vals.sort()
y_ax_upper += len(cls_silhouette_vals)
cmap = cm.get_cmap("Spectral")
rgba = list(cmap(idx/num_clusters)) # rgbaの配列
rgba[-1] = 0.7 # alpha値を0.7にする
plt.barh(
y=range(y_ax_lower, y_ax_upper),
width=cls_silhouette_vals,
height=1.0,
edgecolor='none',
color=rgba)
y_ticks.append((y_ax_lower + y_ax_upper) / 2.0)
y_ax_lower += len(cls_silhouette_vals)
silhouette_avg = np.mean(silhouette_vals)
plt.axvline(silhouette_avg, color='orangered', linestyle='--')
plt.xlabel('silhouette coefficient')
plt.ylabel('cluster')
plt.yticks(y_ticks, cluster_labels + 1)
plt.show()
for i in range(2, 5):
model = KMeans(n_clusters=i, random_state=0, init='random')
model.fit(dfSS)
show_silhouette(model)


参考までにクラスター数を変えた時の結果を示しておく。どれが正解というのはないので,目的に応じた解釈が要求される。


本記事のもくじはこちら: