我在尝试弄明白scipy.cluster.hierarchy.dendrogram
的输出是如何工作的…我原本以为我已经明白了它的工作原理,并且能够利用输出重建树状图,但现在似乎我不再理解它了,或者是Python 3
版本的这个模块中存在bug。
这个回答,如何获取由scipy.cluster.hierarchy生成的树状图的子树,暗示dendrogram
输出的字典提供了dict_keys(['icoord', 'ivl', 'color_list', 'leaves', 'dcoord'])
,这些键的值大小相同,因此你可以使用zip
函数将它们组合,并用plt.plot
重建树状图。
这看起来很简单,实际上我在使用Python 2.7.11
时确实成功了,但是一旦我升级到Python 3.5.1
,我的旧脚本就不再产生相同的结果了。
我开始重新调整我的聚类,以便创建一个非常简单的可重复示例,我认为我可能在Python 3.5.1
的SciPy版本0.17.1-np110py35_1
中发现了一个bug。将使用Scikit-learn
的数据集,因为大多数人从conda分发中拥有这个模块。
为什么这些数据无法对齐?为什么我无法用这种方式重建树状图?
# Initimport pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as sns; sns.set()# Load datafrom sklearn.datasets import load_diabetes# Clusteringfrom scipy.cluster.hierarchy import dendrogram, fcluster, leaves_listfrom scipy.spatial import distancefrom fastcluster import linkage # You can use SciPy one too%matplotlib inline# DatasetA_data = load_diabetes().dataDF_diabetes = pd.DataFrame(A_data, columns = ["attr_%d" % j for j in range(A_data.shape[1])])# Absolute value of correlation matrix, then subtract from 1 for disimilarityDF_dism = 1 - np.abs(DF_diabetes.corr())# Compute average linkageA_dist = distance.squareform(DF_dism.as_matrix())Z = linkage(A_dist,method="average")# I modded the SO code from the above answer for the plot functiondef plot_tree( D_dendro, ax ): # Set up plotting data leaves = D_dendro["ivl"] icoord = np.array( D_dendro['icoord'] ) dcoord = np.array( D_dendro['dcoord'] ) color_list = D_dendro["color_list"] # Plot colors for leaf, xs, ys, color in zip(leaves, icoord, dcoord, color_list): print(leaf, xs, ys, color, sep="\t") plt.plot(xs, ys, color) # Set min/max of plots xmin, xmax = icoord.min(), icoord.max() ymin, ymax = dcoord.min(), dcoord.max() plt.xlim( xmin-10, xmax + 0.1*abs(xmax) ) plt.ylim( ymin, ymax + 0.1*abs(ymax) ) # Set up ticks ax.set_xticks( np.arange(5, len(leaves) * 10 + 5, 10)) ax.set_xticklabels(leaves, fontsize=10, rotation=45) plt.show()fig, ax = plt.subplots()D1 = dendrogram(Z=Z, labels=DF_dism.index, color_threshold=None, no_plot=True)plot_tree(D_dendro=D1, ax=ax)
attr_1 [ 15. 15. 25. 25.] [ 0. 0.10333704 0.10333704 0. ] gattr_4 [ 55. 55. 65. 65.] [ 0. 0.26150727 0.26150727 0. ] rattr_5 [ 45. 45. 60. 60.] [ 0. 0.4917828 0.4917828 0.26150727] rattr_2 [ 35. 35. 52.5 52.5] [ 0. 0.59107459 0.59107459 0.4917828 ] battr_8 [ 20. 20. 43.75 43.75] [ 0.10333704 0.65064998 0.65064998 0.59107459] battr_6 [ 85. 85. 95. 95.] [ 0. 0.60957062 0.60957062 0. ] battr_7 [ 75. 75. 90. 90.] [ 0. 0.68142114 0.68142114 0.60957062] battr_0 [ 31.875 31.875 82.5 82.5 ] [ 0.65064998 0.72066112 0.72066112 0.68142114] battr_3 [ 5. 5. 57.1875 57.1875] [ 0. 0.80554653 0.80554653 0.72066112] b
所以请注意颜色映射不正确。它说[ 15. 15. 25. 25.]
的icoord
对应attr_1
,但根据数值来看,它似乎对应attr_4
。此外,它没有一直延伸到最后一个叶子节点(attr_9
),这是因为icoord
和dcoord
的长度比ivl
标签的数量少1。
print([len(x) for x in [leaves, icoord, dcoord, color_list]]) #[10, 9, 9, 9]
回答:
icoord
、dcoord
和color_list
描述的是连接,而不是叶子节点。icoord
和dcoord
给出每个连接在图表中的“拱形”(即倒U形或J形)的坐标,而color_list
则是这些拱形的颜色。在完整的图表中,icoord
等的长度会比ivl
的长度少1,正如你所观察到的那样。
不要试图将ivl
列表与icoord
、dcoord
和color_list
列表对齐。它们关联的是不同的东西。