Pythonで階層的クラスタリングをするときは、scipy.cluster.hierarchyを使うのが簡単なのですが、先日ちょっと間違えてしまいまして。
準備
scipyやmatplotlibを使います(インストールは済んでいるとして)。
import scipy import numpy import scipy.spatial.distance as distance from matplotlib.pyplot import show from scipy.cluster.hierarchy import linkage, dendrogram from random import random
特徴ベクトルの長さがdimのデータをn個ランダムに作り、実験用のデータとします。
n = 100 dim = 10 data = [[random() for i in range(dim)] for i in range(n)]
方法1
非類似度の指標とクラスタ連結法を指定して、クラスタリングを実行します。
result1 = linkage(data, metric = 'chebyshev', method = 'average')
デンドログラムを描きます。
dendrogram(result1) show()
このように、データをそのまま使ってクラスタリングするのが簡単です。
方法2
データ間の距離を与えてクラスタリングすることもできます。ただし、データ間の距離は、n行n列の距離行列ではなく、pdist()で作られるベクトルで表現します。
dArray1 = distance.pdist(data, metric = 'chebyshev') result2 = linkage(dArray1, method = 'average') assert (result1 == result2).all()#同じ結果
補足
pdist()で作られるベクトルは、次のようなものです。
dArray2 = [] for i in range(n - 1): for j in range(i + 1, n): dArray2.append(distance.chebyshev(data[i], data[j])); assert (dArray1 == dArray2).all()#同じもの
このベクトルは、n行n列の距離行列からsquareform()で作ることもできます。
dMatrix1 = numpy.zeros([n, n]) for i in range(n): for j in range(n): dMatrix1[i, j] = distance.chebyshev(data[i], data[j]) dArray3 = distance.squareform(dMatrix1) assert (dArray1 == dArray3).all()#同じもの
逆に、pdist()の結果からn行n列の距離行列を作ることもできます。
dMatrix2 = distance.squareform(dArray1) assert (dMatrix1 == dMatrix2).all()#同じもの
まとめ
- 階層的クラスタリングは、linkage(data)あるいはlinkage(距離のベクトル表現)で行う。(ちょっとわかりにくい仕様ですね。)
- 距離のベクトル表現は、pdist(data)あるいはsquareform(n行n列の距離行列)で作れる。
- n行n列の距離行列は、squareform(距離のベクトル表現)で作れる。
間違い
linkage(n行n列の距離行列)でクラスタリングするのは間違いです。こうすると、行列の各行がクラスタリングの対象データになってしまいます。
しかし、距離行列の各行は、元データのそれぞれが、他のデータとどの程度似ていないかを表現したものなので、これ自体を特徴ベクトルと見なすこともできます。ですから、次のような「間違った」方法も、自覚して使うなら間違いではありません。
result3 = linkage(dMatrix1, metric = 'chebyshev', method = 'average')
さらに、非類似度の指標としてチェビシェフ距離を使うなら、「正しい」方法と同じ結果になります。
assert (result1 == result3).all()#同じ結果