我一直在尝试使用层次聚类
,在R
中非常简单hclust(as.dist(X),method="average")
。我在Python
中也找到了一个相当简单的办法,但对于我的输入距离矩阵有些困惑。
我有一个相似性矩阵(DF_c93tom
,还有一个较小的测试版本DF_sim
),我将其转换为一个非相似性矩阵DF_dissm = 1 - DF_sim
。
我将此作为输入传递给scipy
中的linkage
函数,但文档中说它接受方形或三角形矩阵。我输入下三角
、上三角
和方形矩阵
时得到了不同的聚类结果。这是为什么呢?文档中说它需要上三角矩阵,但下三角聚类看起来非常相似。
我的问题是,为什么所有这些聚类结果都不同?哪个是正确的?
这是linkage
函数输入距离矩阵的文档
y : ndarray一个压缩或冗余的距离矩阵。压缩距离矩阵是一个包含距离矩阵上三角部分的平面数组。
这是我的代码:
import matplotlib.pyplot as pltimport seaborn as snsimport numpy as npimport pandas as pdfrom scipy.cluster.hierarchy import dendrogram, linkage%matplotlib inline#测试数据DF_sim = DF_c93tom.iloc[:10,:10] #相似性矩阵DF_sim.columns = DF_sim.index = range(10) #print(DF_test)# 0 1 2 3 4 5 6 7 8 9# 0 1.000000 0 0.395833 0.083333 0 0 0 0 0 0# 1 0.000000 1 0.000000 0.000000 0 0 0 0 0 0# 2 0.395833 0 1.000000 0.883792 0 0 0 0 0 0# 3 0.083333 0 0.883792 1.000000 0 0 0 0 0 0# 4 0.000000 0 0.000000 0.000000 1 0 0 0 0 0# 5 0.000000 0 0.000000 0.000000 0 1 0 0 0 0# 6 0.000000 0 0.000000 0.000000 0 0 1 0 0 0# 7 0.000000 0 0.000000 0.000000 0 0 0 1 0 0# 8 0.000000 0 0.000000 0.000000 0 0 0 0 1 0# 9 0.000000 0 0.000000 0.000000 0 0 0 0 0 1#非相似性矩阵DF_dissm = 1 - DF_sim#冗余矩阵#np.tril(DF_dissm).T == np.triu(DF_dissm)#所有值为True#方形和三角矩阵的层次聚类fig_1 = plt.figure(1)plt.title("方形")Z_square = linkage((DF_dissm.values),method="average")dendrogram(Z_square)fig_2 = plt.figure(2)plt.title("上三角")Z_triu = linkage(np.triu(DF_dissm.values),method="average")dendrogram(Z_triu)fig_3 = plt.figure(3)plt.title("下三角")Z_tril = linkage(np.tril(DF_dissm.values),method="average")dendrogram(Z_tril)plt.show()
回答:
当一个二维数组作为第一个参数传递给scipy.cluster.hierarchy.linkage时,它被视为一系列观测值,并且scipy.spatial.pdist
被使用将其转换为一系列观测值之间的成对距离。
有一个github问题讨论这种行为,因为这意味着传递一个“距离矩阵”如DF_dissm.values
(静默地)会产生一个错误的结果。
所以这个问题的结论是这些
Z_square = linkage((DF_dissm.values),method="average")Z_triu = linkage(np.triu(DF_dissm.values),method="average")Z_tril = linkage(np.tril(DF_dissm.values),method="average")
都不会产生期望的结果。相反,应该使用
-
h, w = arr.shapeZ = linkage(arr[np.triu_indices(h, 1)], method="average")
-
或者
spatial.distance.squareform
:from scipy.spatial import distance as ssdZ = linkage(ssd.squareform(arr), method="average")
-
或者对原始点应用
spatial.distance.pdist
:Z = hierarchy.linkage(ssd.pdist(points), method="average")
-
或者传递二维数组
points
:Z = hierarchy.linkage(points, method="average")
import matplotlib.pyplot as pltimport numpy as npfrom scipy.cluster import hierarchy as hierfrom scipy.spatial import distance as ssdnp.random.seed(2016)points = np.random.random((10, 2))arr = ssd.cdist(points, points)fig, ax = plt.subplots(nrows=4)ax[0].set_title("压缩上三角")Z = hier.linkage(arr[np.triu_indices(arr.shape[0], 1)], method="average")hier.dendrogram(Z, ax=ax[0])ax[1].set_title("方形")Z = hier.linkage(ssd.squareform(arr), method="average")hier.dendrogram(Z, ax=ax[1])ax[2].set_title("pdist")Z = hier.linkage(ssd.pdist(points), method="average")hier.dendrogram(Z, ax=ax[2])ax[3].set_title("观测值序列")Z = hier.linkage(points, method="average")hier.dendrogram(Z, ax=ax[3])plt.show()