メインコンテンツへスキップ
見出し画像

書記が数学やるだけ#576 k-平均法

    非階層型クラスタリングの代表例であるk-平均法について見ていく。


    問題

    スクリーンショット 2022-09-16 8.00.50


    説明

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

    スクリーンショット 2022-09-16 7.51.08


    スクリーンショット 2022-09-16 7.51.59


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

    スクリーンショット 2022-09-16 7.54.42


    解答

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

    数学やるだけ解答#576_page-0001


    では実際のデータで見てみる。

    #必要なライブラリ
    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)
    画像7


    まずは標準化をしておく。

    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)
    画像6


    k-平均法を実行,まずは実際のデータ通りクラスター数3でやってみる。

    #k-平均法の実行
    from sklearn.cluster import KMeans
    model = KMeans(n_clusters=3, random_state=0)
    model.fit(dfSS)
    clusters = model.predict(dfSS)
    clusters

    array([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
    スクリーンショット 2022-09-16 8.09.56


    以下で結果の可視化をしておく。

    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()
    画像9


    それでは,検証としてエルボー法やシルエット分析を行ってみる。

    エルボー分析からは,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()
    画像10


    シルエット分析からは,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)
    画像11


    画像12


    画像13


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

    画像14


    画像15


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


    あなたへのおすすめ