コーフェン相関係数を用いて、階層的クラスタリングにおける最適な距離と方法を考える。

#はじめに
階層的クラスタリングを行うときに迷うのが、どの距離と方法を使うのがよいのかという点です。正解の用意されていないクラスタリングでは、特にです。
今回は、距離と方法ごとにコーフェン相関係数を計算して、クラスタリングの妥当性を調べてみます。この値が１に近いほど良いとされています。（詳しくは[こちら](https://jp.mathworks.com/help/stats/cophenet.html)）

#データの準備
今回は、irisのデータセットを使いました。

```Python:
# irisのデータセットを得る。
from sklearn import datasets
dataset = datasets.load_iris()
dataset_data = dataset.data
dataset_target = dataset.target
target_names = dataset.target_names
dataset_labels = target_names[dataset_target]

# データの標準化を行う。
from sklearn.preprocessing import scale
data = scale(dataset_data)
```

# コーフェン相関係数の計算
まず、クラスタリングで使いそうな距離や方法をリストアップします。データの形に合うものをいくつか用意します。

```Python:
METORICS = ['euclidean', 'minkowski', 'cityblock', 'seuclidean',
            'sqeuclidean', 'cosine', 'correlation', 'hamming',
            'jaccard', 'chebyshev', 'canberra', 'braycurtis',
            'mahalanobis']

METHODS = ['single', 'complete', 'average', 'weighted', 
           'centroid', 'median', 'ward']

# minkowski距離に使うノルム。デフォルトだと2で、ユークリッド距離となるので、とりあえず3。
P_NORM = 3
```

コーフェン相関係数の計算には`scipy`の`cophenet`を使いました。（詳しくは[こちら](https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.cluster.hierarchy.cophenet.html#scipy.cluster.hierarchy.cophenet)）


```Python:
# 距離と方法を変えてコーフェン相関係数(CPCC）を計算する。
import itertools
from scipy.spatial.distance import pdist
from scipy.cluster.hierarchy import linkage, cophenet

result = []  
for metric, method in itertools.product(METORICS, METHODS):
    distance_matrix = pdist(X=data, metric=metric, p=P_NORM)
    linkage_matrix = linkage(y=distance_matrix, method=method)
    cpcc, _ = cophenet(linkage_matrix, distance_matrix)
    result.append([metric, method, cpcc])
```

データ処理のために、`pandas`の`Dataframe`に計算結果を入れます。コーフェン相関係数が最大となった距離と方法を得ます。

```Python:
import pandas as pd
cpcc_df = pd.DataFrame(result, columns=['metric', 'method', 'CPCC'])

# コーフェン相関係数が最大である距離と方法を得る
best_metoric = cpcc_df.loc[cpcc_df['CPCC'].idxmax(), 'metric']
best_method = cpcc_df.loc[cpcc_df['CPCC'].idxmax(), 'method']

print ("距離：{0}, 方法：{1} でコーフェン相関係数が最大。".format(best_metoric,best_method))
```

実行結果は、以下のとおり。

```
距離：correlation, 方法：average でコーフェン相関係数が最大。
```

これだけではよくわからないので、`seaborn`の`heatmap`を使って、全体を見てみました。

```Python
# DataFrameを整形。
cpcc_df = cpcc_df.set_index(keys=['metric', 'method']).unstack()
cpcc_df.columns = cpcc_df.columns.droplevel()


# heatmapで表示。
import matplotlib.pyplot as plt
import seaborn as sns

fig, ax = plt.subplots(figsize=(8.5, 8))
ax = sns.heatmap(data=cpcc_df, annot=True)
fig.show()
```

![heatmap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424524/da7eb6be-d70b-f99f-ee84-dab85428560b.png)

`heatmap`を見てみると、方法よりも距離の方がコーフェン相関係数に影響している傾向があります。この傾向は、クラスタリングするデータによって変わってくるかもしれません。


#デンドログラムの作図
計算結果から得たベストな距離と方法でデンドログラムを描いて完成です。

```Python:
# コーフェン相関係数が最大である距離と方法でデンドログラムを描く。
from scipy.cluster.hierarchy import linkage, dendrogram

best_linkage = linkage(y=data, metric=best_metoric, method=best_method)

fig, ax = plt.subplots(figsize=(5,13))
ax = dendrogram(Z=best_linkage,
                orientation='right',
                labels=dataset_labels)
fig.show()
```

![cpcc_dendrogram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424524/5500514e-ba6c-3174-f5e6-f301085c7e92.png)

#最後に
今回計算したコーフェン相関係数も、これだけで完璧なクラスタリングができるわけではなく、指標の一つでしかありません。複数の指標を使ったクラスタリングを目指して勉強していきたいと思います。最後までお付き合いいただき、ありがとうございました。