静かなる名辞

pythonとプログラミングのこと


【python】クラスタリング結果を積み上げ棒グラフで可視化する

はじめに

 ラベル付きデータをクラスタリングすることがよくあります(そんな頻繁にあるか? まあ、クラスタリングの使い方次第でたまにはあるからこうして記事にしている訳ですが)。

 各クラスタの中身がどんなラベルで構成されているのか、知りたくなります。積み上げ棒グラフで出てくれると嬉しいですね(嬉しさがわからない方も読み進めて頂ければわかるので大丈夫)。

 pythonでの積み上げ棒グラフの描きをググると、matplotlibを駆使した怖い(大変そうな)描き方がいくらでも出てくるのですが、そんなことで苦労したくないので簡単なやり方でやります。

やりかた

 こちらを参考にしました。
python - pandasのデータフレームから積み上げ棒グラフを作りたい - スタック・オーバーフロー
 ぜんぶpandasの機能でできるらしいです。素敵。要するにクロス集計してplotしてやれば良い、ということのようです。

やってみた

 irisとdigitsをクラスタリングし、上の方法を参考にグラフ化してみます。

 ソースコード

# coding: UTF-8

import numpy as np
import pandas as pd

from sklearn.datasets import load_digits, load_iris
from sklearn.cluster import KMeans

import matplotlib.pyplot as plt

def visualize(y_true, y_cl, y_names, filename="result.png"):
    y_true = np.array([y_names[y] for y in y_true])

    df = pd.crosstab(y_cl, y_true, 
                     rownames=["cluster number"], colnames=["true label"])
    print(df)

    plt.figure()
    df.plot(kind='bar',stacked=True, legend=False)
    plt.legend(bbox_to_anchor=(1.13, 1), loc="upper right")
    plt.savefig(filename)

def main():
    iris = load_iris()
    km = KMeans(n_clusters=3, n_init=30, max_iter=1000)
    cluster_labels = km.fit_predict(iris.data)
    print("iris")
    visualize(iris.target, cluster_labels, 
              iris.target_names, filename="iris.png")

    digits = load_digits()
    km = KMeans(n_clusters=10, n_init=30, max_iter=1000)
    cluster_labels = km.fit_predict(digits.data)
    print("digits")
    visualize(digits.target, cluster_labels, 
              digits.target_names, filename="digits.png")

if __name__ == "__main__":
    main()

 とりあえず、クロス集計した結果のDFをテキストで吐いてみたので、そっちから見せます。

iris
true label      setosa  versicolor  virginica
cluster number                               
0                   50           0          0
1                    0          48         14
2                    0           2         36
digits
true label        0    1    2    3    4    5    6    7    8    9
cluster number                                                  
0               177    0    1    0    0    0    1    0    0    0
1                 0   24  148    0    0    0    0    0    3    0
2                 0    0    3    7   10    0    0  177    5    8
3                 1    0    0    0  166    2    0    0    0    0
4                 0    1   13  155    0    2    0    0    2    6
5                 0    1    0    2    0  136    0    0    4    5
6                 0    2    0    0    0    1  177    0    2    0
7                 0    0    2   12    0   41    0    0   51  140
8                 0  100    8    7    2    0    3    2  101    1
9                 0   54    2    0    3    0    0    0    6   20

 なるほど。まあ、なんとなくどんな状態になっているのかはこの表からもわかります。

 そしてお待ちかねのグラフはこちら。

irisの結果
iris
digitsの結果
digits
 わかりやすいですね。この記事で言いたいことは、この絵を簡単に得られるpandasは便利、ということだけです。

 一応グラフの説明をすると、このグラフは各クラスタに割り振られたデータのラベルの件数を表しています。そして、たとえばirisのグラフからは、setosaは完全に一つのクラスタを形成していますが、versicolorとviriginicaは綺麗にクラスタには分かれず混ざる、ということがわかります。versicolorとviriginicaが似ている、というかベクトル空間上で近くにいる、という知見が得られる訳です。

まとめ

 pandasは独自の世界観があって正直苦手なんですが、たまに便利に使えることがあるなぁと思いました(小並感)。