Python 自動でクラス数を決定してクラスタリングする「x-means」と「g-means」

 クラスタリングクラスター分析と言えば、k-meansが有名です。しかし、予めクラス(分類)数を指定する必要があります。この対策として、分類数を自動で決定するアルゴリズムはいくつか存在していて、「x-means」,「g-means」,「Star Clustering」などがあります。
 本記事では、「x-means」と「g-means」の雛形コードと実行結果の比較例を載せました。この2つは、pyclusteringをインストールすることで使用できます。

■インストール方法

pipの場合

pip3 install pyclustering

Anaconda環境下の場合

conda install -c conda-forge pyclustering

■分析に使用した例題データ

 scikit-learnに同梱されているワインデータセットを用いました。本来は、機械学習の分類用に用意されたデータセットで、説明変数(特徴量)が13項目あって、予め3つに分類された「target」があります。詳細は次のリンク先を参照sklearn.datasets.load_wine — scikit-learn 0.24.2 documentation

 本記事では、分類ではなくてクラスター分析のため、教師データは使わずに分類するため、上記「target」を用いた分析はしません。(分析後の比較だけに使います)

 

クラスタリングの実施例
先に、実施例について説明します。分析時に使用した説明変数の数が2, 3, 13項目の3つ場合について順番に説明します。

 下図は、1つ目の例です。クラスター分析に特徴量2項目だけを用いた場合です。
下図左は元データで3つのカテゴリに分かれています(3つのクラス分類)。一方、下図中央のx-meansでの分析結果は4つにクラス分類され、下図右のg-meansの場合では2つにクラス分類されました。分類数が異なる結果になりました。しかし、それら集合体の分布は違和感はなく妥当に仕分けされたようにみえます。本来は正解データはわからないため、技術者本人が用途に応じて判断することになります。

f:id:HK29:20210503134734p:plain

 下図は、2つ目の例です。クラスター分析に特徴量「3項目」を用いた場合です。
x-meansは3つにクラス分類され、g-meansは4つにクラス分類された結果です。

f:id:HK29:20210503135319p:plain

 下図は、3つ目の例です。クラスター分析に特徴量全て13項目を用いた場合です。
x-meansは3つにクラス分類され、g-meansは7つにクラス分類された結果です。

f:id:HK29:20210503135610p:plain

 上記3つの例は、クラスタリングに使用する説明変数の数を変更して検証しました。実は、本コードでは前処理としてデータを標準化しています。この前処理の有無でも結果は変わります。他には、クラスター分析したい説明変数間に桁が著しくことなるデータ群、例えば、対数データ群や何桁も小さい小数データ群などがある場合は標準化した方が吉です。また、実務においては外れ値が存在する場合も頭に入れておき、場合によってはそれらを削除する判断が必要なこともありえます。

 

■本プログラム

本コードはx-meansの場合の例です。g-meansを使用したい場合は、xmeansの箇所をgmeansに置換するだけでOKです。

#!/usr/bin/env python
# coding: utf-8

# In[1]:


from sklearn import datasets, preprocessing
import pandas as pd
from pyclustering.cluster.xmeans import xmeans
from pyclustering.cluster.center_initializer import kmeans_plusplus_initializer
#from pyclustering.cluster import cluster_visualizer
import matplotlib.pyplot as plt

wine_data = datasets.load_wine()
# 説明変数のみをpandasデータフレームで読み出し
df = pd.DataFrame(wine_data.data, columns=wine_data.feature_names)
df


# In[2]:


# クラスタリングするデータの抽出
#X = df[['flavanoids', 'color_intensity']].copy()
X = df[['alcohol', 'flavanoids', 'color_intensity']].copy()
#X = df.copy()

# 標準化(平均0, 標準偏差1)インスタンス
scaler = preprocessing.StandardScaler()

# 標準化を実行し変数に代入する
scaler.fit(X)
scaled_X = scaler.transform(X)
scaled_X


# In[3]:


# クラスタリングの初期条件
amount_initial_centers = 2 # 最小2クラス以上に分類する
initial_centers = kmeans_plusplus_initializer(scaled_X, amount_initial_centers).initialize()

# クラスタリングの実行
xmeans_instance = xmeans(scaled_X, initial_centers=initial_centers, )
xmeans_instance.process()

# クラスタリングした要素番号を取得
clusters = xmeans_instance.get_clusters()
clusters


# In[4]:


# クラスタリング数
len(clusters)


# In[5]:


# クラスタリングした集合体の個々の値を戻す
inversed_X = scaler.inverse_transform(scaled_X)
inversed_X


# In[6]:


# クラスタリングの重心を抽出する
centers = xmeans_instance.get_centers()
centers


# In[7]:


# クラスタリングの重心値を戻す
inversed_centers = scaler.inverse_transform(centers)
inversed_centers


# In[8]:


# 描画
'''
visualizer = cluster_visualizer()
visualizer.append_clusters(clusters, inversed_X, markersize = 10)
visualizer.append_cluster(inversed_centers,
                          None, marker='*',
                          markersize=20,
                          color='black')
visualizer.show()
'''


# In[9]:


# クラスタリング結果を元データへ「pred」の列名で追加する
df_list = []
ABC_list = [chr(ord("A")+i) for i in range(26)]
for my_list, my_label in zip(clusters, ABC_list):
    df_buf = df.iloc[my_list].copy()
    df_buf['pred'] = my_label
    df_list.append(df_buf)
DF = pd.concat(df_list)
DF.sort_index(axis='index', inplace=True)
DF


# In[10]:


# 正解のデータ「target」も結合する
df_target = pd.DataFrame(wine_data.target, columns=['target'])

DF2 = pd.concat([DF, df_target], axis=1)
DF2.to_csv('xmeans.csv')
DF2


# In[11]:


import seaborn as sns

y_name = 'pred'
name_list = ["alcohol", "flavanoids","color_intensity", y_name]

# 行列散布図
sns.set_context('talk')
ax = sns.pairplot(
    DF2[name_list],
    hue = y_name, # 凡例に表示したい列名を指定(カテゴリ変数)
    palette = 'gnuplot2', # 'tab10' 'magma' 'cool' 'bar' 'gnuplot2'
    kind = 'reg', # 線形近似線を記入
    markers = '.',
    diag_kind = 'kde',
    diag_kws = dict(shade = True),
)
plt.savefig("xmeans_pred.png")
plt.close()


# In[12]:


y_name = 'target'
name_list = ["alcohol", "flavanoids","color_intensity", y_name]

# 行列散布図
sns.set_context('talk')
ax = sns.pairplot(
    DF2[name_list],
    hue = y_name, # 凡例に表示したい列名を指定(カテゴリ変数)
    palette = 'gnuplot2', # 'tab10' 'magma' 'cool' 'bar' 'gnuplot2'
    kind = 'reg', # 線形近似線を記入
    markers = '.',
    diag_kind = 'kde',
    diag_kws = dict(shade = True),
)
plt.savefig("xmeans_target.png")
plt.close()


# In[ ]:

以上

<広告>