階層的クラスタリングでは距離や類似度をもとにクラスターを作っていくわけだけど、距離や類似度にはいくつか種類があって、分析に適したものを選択することになる。距離としてはユークリッド距離が使われる例がたくさんある一方で、テキスト分析ではコサイン類似度がよく使われるらしい。じゃぁ実際のところ、この2つはどう違うのだろうかというのが疑問で、実際に同じデータをPythonのScipyで階層的クラスタリングして比べてみた。
Bash on Ubuntu on WindowsとJupyter Notebook。
ユークリッド距離は、一般的に言うところの2点間の距離なので感覚的に理解できる。今回はScipyで階層的クラスタリングをするけれど、デフォルトはユークリッド距離になっている。一方のコサイン類似度は2つのベクトルによる角度をθとしたときのcosθで、θが0度のときは1,90度のときは0になる。つまり角度が小さいほど類似度が1に近づいて高くなる。コサイン類似度(高校数学の美しい物語)の説明がわかりやすい。
階層的クラスタリングするためのデータを準備する。データはscikit-learnのirisデータセットを使う。まずはデータセットをPandasのDataframeにして正規化する。
以下のようなDataFrameになる。
Scipyのlinkageで階層的クラスタリングを行い、dendrogramでデンドログラム(樹形図)を作成する。まずはユークリッド距離から。比較しやすいように使うデータはpetal length (cm)とpetal width (cm) の2次元のみにする。クラスタリング手法はウォード法。
次はユークリッド距離の代わりにコサイン類似度で階層的クラスタリングをする。
デンドログラムだとわかりずらいので散布図で比較してみる。
ユークリッド距離を使ったときの結果。
コサイン類似度を使ったときの結果。
コサイン類似度のときはベクトルの角度で分割されているのがわかる。
全体コードはこちら。
環境
Bash on Ubuntu on WindowsとJupyter Notebook。
ユークリッド距離とコサイン類似度
ユークリッド距離は、一般的に言うところの2点間の距離なので感覚的に理解できる。今回はScipyで階層的クラスタリングをするけれど、デフォルトはユークリッド距離になっている。一方のコサイン類似度は2つのベクトルによる角度をθとしたときのcosθで、θが0度のときは1,90度のときは0になる。つまり角度が小さいほど類似度が1に近づいて高くなる。コサイン類似度(高校数学の美しい物語)の説明がわかりやすい。
データの準備
階層的クラスタリングするためのデータを準備する。データはscikit-learnのirisデータセットを使う。まずはデータセットをPandasのDataframeにして正規化する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.preprocessing import MinMaxScaler % matplotlib inline import matplotlib.pyplot as plt def iris_df(): # scikit-learnのirisデータセット読み込み iris = load_iris() # irisデータセットをPandasのDetaFrameに変換 df = pd.DataFrame(data = np.c_[iris[ 'data' ], iris[ 'target' ]], columns = iris[ 'feature_names' ] + [ 'target' ]) # 正規化 items = df.columns index = df.index df = pd.DataFrame(MinMaxScaler().fit_transform(df)) df.columns = items #カラム名を再設定 df.index = index # インデックス名を再設定 return df if __name__ = = '__main__' : df = iris_df() print (df) |
以下のようなDataFrameになる。

Pythonで階層的クラスタリング
Scipyのlinkageで階層的クラスタリングを行い、dendrogramでデンドログラム(樹形図)を作成する。まずはユークリッド距離から。比較しやすいように使うデータはpetal length (cm)とpetal width (cm) の2次元のみにする。クラスタリング手法はウォード法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.preprocessing import MinMaxScaler from scipy.spatial.distance import pdist from scipy.cluster.hierarchy import dendrogram, linkage % matplotlib inline import matplotlib.pyplot as plt def draw_dendrogram(Z, labels): # デンドログラムの作成 plt.figure(figsize = ( 14 , 5 )) plt.ylabel( 'Distance' ) dendrogram( Z, leaf_rotation = 90. , leaf_font_size = 8. , labels = labels ) plt.show() if __name__ = = '__main__' : df = iris_df() print (df) # petal length (cm)とpetal width (cm) で階層的クラスタリング Z = linkage(pdist(df[df.columns[ 2 : 4 ]].as_matrix(), metric = 'euclidean' ), method = 'ward' ) draw_dendrogram(Z, df.index.values) |

次はユークリッド距離の代わりにコサイン類似度で階層的クラスタリングをする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.preprocessing import MinMaxScaler from scipy.spatial.distance import pdist from scipy.cluster.hierarchy import dendrogram, linkage % matplotlib inline import matplotlib.pyplot as plt def draw_dendrogram(Z, labels): # デンドログラムの作成 plt.figure(figsize = ( 14 , 5 )) plt.ylabel( 'Distance' ) dendrogram( Z, leaf_rotation = 90. , leaf_font_size = 8. , labels = labels ) plt.show() if __name__ = = '__main__' : df = iris_df() print (df) # petal length (cm)とpetal width (cm) で階層的クラスタリング Z = linkage(pdist(df[df.columns[ 2 : 4 ]].as_matrix(), metric = 'cosine' ), method = 'ward' ) draw_dendrogram(Z, df.index.values) |

階層的クラスタリングの結果を比較してみる
デンドログラムだとわかりずらいので散布図で比較してみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.preprocessing import MinMaxScaler from scipy.spatial.distance import pdist from scipy.cluster.hierarchy import dendrogram, linkage, fcluster % matplotlib inline import matplotlib.pyplot as plt def draw_scatter(df): # targetごとに色分けしてプロットする groups = df.groupby( 'fcluster' ) for status, group in groups: plt.plot(group[df.columns[ 2 ]], group[df.columns[ 3 ]], marker = 'o' , linestyle = '', ms = 4 ) plt.grid( True ) plt.show() if __name__ = = '__main__' : df = iris_df() print (df) metric_t = ( 'euclidean' , 'cosine' ) # ユークリッド距離、コサイン類似度 th_t = ( 4.0 , 0.6 ) # 分割する距離の閾値 for metric, th in zip (metric_t, th_t): # petal length (cm)とpetal width (cm) で階層的クラスタリング Z = linkage(pdist(df[df.columns[ 2 : 4 ]].as_matrix(), metric = metric), method = 'ward' ) draw_dendrogram(Z, df.index.values) # 距離をもとにフラットクラスターを作成 df[ 'fcluster' ] = fcluster(Z, th, criterion = 'distance' ) draw_scatter(df) |
ユークリッド距離を使ったときの結果。

コサイン類似度を使ったときの結果。

コサイン類似度のときはベクトルの角度で分割されているのがわかる。
全体コードはこちら。
0 件のコメント:
コメントを投稿