雑記: sklearn.cluster.KMeans で k-means 法

sklearn.cluster.KMeans をつかってみたいときとかあると思います。iris データにつかうと以下のようになります。

# -*- coding: utf-8 -*-
import pandas
from sklearn import datasets
from sklearn.cluster import KMeans
from sklearn.metrics import confusion_matrix

if __name__ == "__main__":
  iris = datasets.load_iris()
  predict = KMeans(n_clusters=3).fit(iris.data)

  mat = pandas.DataFrame(confusion_matrix(iris.target, predict.labels_))
  mat.index = iris.target_names
  print(mat)
             0   1   2
setosa       0  50   0
versicolor  48   0   2
virginica   14   0  36

setosa はすべてクラスタ1に分類され(かつ、クラスタ1に他の品種は混ざっていない)、一方、virginica はクラスタ0とクラスタ2に分裂してしまったことがわかります。各品種が分類された最大クラスタが対角線上に並んでいないと見にくいという人は、以下のように列を入れ替えてください。

  mat = mat.loc[:,mat.idxmax(axis=1)]
  mat.columns = range(iris.target_names.shape[0])
  print(mat)
             0   1   2
setosa      50   0   0
versicolor   0  48   2
virginica    0  14  36

クラスタリングした結果、各クラスタの重心はどこになったかも確認できます(以下)。

  print(predict.cluster_centers_)
[[ 6.85        3.07368421  5.74210526  2.07105263]
 [ 5.006       3.418       1.464       0.244     ]
 [ 5.9016129   2.7483871   4.39354839  1.43387097]]

初期クラスタ重心を与えることもできます。
以下のようにすれば元データの 0 番目、50 番目、100 番目を初期クラスタ重心にできます。

# -*- coding: utf-8 -*-
import numpy
import pandas
from sklearn import datasets
from sklearn.cluster import KMeans
from sklearn.metrics import confusion_matrix

if __name__ == "__main__":
  iris = datasets.load_iris()
  initial_centers = numpy.array([iris.data[0], iris.data[50], iris.data[100]])
  predict = KMeans(n_clusters=3, init=initial_centers).fit(iris.data)

  mat = pandas.DataFrame(confusion_matrix(iris.target, predict.labels_))
  mat.index = iris.target_names
  print(mat)
  print(predict.n_iter_)

ただし、これを実行すると以下のように警告文が出てきます。これの意味は、KMeans はクラスタ初期化方法が k-means++(デフォルト)や random の場合はデフォルトで n_init=10 通りの初期値からのクラスタリングを試み、最もよいクラスタリング結果を出力します(モーメントがよく更新され続けた回をもっともよいクラスタリング結果とする)。しかし、初期クラスタ重心が明示的に与えられたときにはその他の初期化を試みるのはおかしいので、デフォルトの n_init=10 は無視して1回しかクラスタリングを試みませんよという意味です。その代わりにというか、クラスタリングイテレーション回数 n_iter_ が返却されます。クラスタ重心は3回更新されるのみだったようです。

/usr/local/lib/python2.7/site-packages/sklearn/cluster/k_means_.py:889: RuntimeWarning:
 Explicit initial center position passed: performing only one init in k-means
 instead of n_init=10
  return_n_iter=True)
             0   1   2
setosa      50   0   0
versicolor   0  48   2
virginica    0  14  36
3