我想使用matplotlib绘制树状图而不使用scipy。一个类似的问题has been posted here;但是,标记的解决方案建议使用scipy,而其他答案中的链接则建议使用ETE。使用this example,我已经验证了我自己的方法(即非scipy方法)使用单链接标准应用聚集层次聚类的准确性。
使用上面链接的相同示例,我具有创建我自己的树状图的必要参数。原始的distance_matrix
由以下方式给出:
.. DISTANCE MATRIX (SHAPE=(6, 6)):
[[ 0 662 877 255 412 996]
[662 0 295 468 268 400]
[877 295 0 754 564 0]
[255 468 754 0 219 869]
[412 268 564 219 0 669]
[996 400 0 869 669 0]]
使用distance_matrix
的掩码数组,以使上方的对角线条目不算作最小值。原始distance_matrix
的掩码如下:
.. MASKED (BEFORE) DISTANCE MATRIX (SHAPE=(6, 6)):
[[-- 662 877 255 412 996]
[662 -- 295 468 268 400]
[877 295 -- 754 564 0]
[255 468 754 -- 219 869]
[412 268 564 219 -- 669]
[996 400 0 869 669 --]]
distance_matrix
在算法的每次迭代中就地更改。算法完成后,distance_matrix
的计算公式为:
.. MASKED (AFTER) DISTANCE MATRIX (SHAPE=(1, 1)):
[[--]]
级别(每次合并的最小距离)由下式给出:
.. 5 LEVELS:
[138, 219, 255, 268, 295]
我们还可以在每次迭代时查看合并数据点的索引;这些索引与原始distance_matrix
相对应,因为缩小尺寸会改变索引位置。这些索引由以下方式给出:
.. 5x2 LOCATIONS:
[(2, 5), (3, 4), (0, 3), (0, 1), (0, 2)]
从这些索引中,树状图的xtick标签的顺序按时间顺序为:
.. 6 XTICKLABELS
[2 5 3 4 0 1]
关于链接的示例
0 = BA
1 = FI
2 = MI
3 = NA
4 = RM
5 = TO
使用这些参数,我想生成一个树状图,如下图所示(从链接的示例中借用):
我尝试使用matplotlib复制此树状图的尝试如下:
fig, ax = plt.subplots()
for loc, level in zip(locations, levels):
x = np.array(loc)
y = level * np.ones(x.size)
ax.step(x, y, where='mid')
ax.set_xticks(xticklabels)
# ax.set_xticklabels(xticklabels)
plt.show()
plt.close(fig)
我在上面的尝试产生了下图:
我意识到我必须对xticklabels
进行重新排序,以使第一个合并点出现在右边缘,随后的每个合并点都向左移动;这样做必然意味着要调整连接线的宽度。另外,我使用的是ax.step
而不是ax.bar
,以便使线条看起来更有条理(与各处的矩形条相对);我唯一想做的就是使用ax.axhline
和ax.axvline
绘制水平和垂直线。我希望有一种更简单的方法来完成我想要的。是否有使用matplotlib的简单方法?
答案 0 :(得分:1)
虽然肯定更容易依靠scipy
,但这是我“手动”执行的方式,即没有它:
import matplotlib.pyplot as plt
import numpy as np
def mk_fork(x0,x1,y0,y1,new_level):
points=[[x0,x0,x1,x1],[y0,new_level,new_level,y1]]
connector=[(x0+x1)/2.,new_level]
return (points),connector
levels=[138, 219, 255, 268, 295]
locations=[(2, 5), (3, 4), (0, 3), (0, 1), (0, 2)]
label_map={
0:{'label':'BA','xpos':0,'ypos':0},
1:{'label':'FI','xpos':3,'ypos':0},
2:{'label':'MI','xpos':4,'ypos':0},
3:{'label':'NA','xpos':1,'ypos':0},
4:{'label':'RM','xpos':2,'ypos':0},
5:{'label':'TO','xpos':5,'ypos':0},
}
fig,ax=plt.subplots()
for i,(new_level,(loc0,loc1)) in enumerate(zip(levels,locations)):
print('step {0}:\t connecting ({1},{2}) at level {3}'.format(i, loc0, loc1, new_level ))
x0,y0=label_map[loc0]['xpos'],label_map[loc0]['ypos']
x1,y1=label_map[loc1]['xpos'],label_map[loc1]['ypos']
print('\t points are: {0}:({2},{3}) and {1}:({4},{5})'.format(loc0,loc1,x0,y0,x1,y1))
p,c=mk_fork(x0,x1,y0,y1,new_level)
ax.plot(*p)
ax.scatter(*c)
print('\t connector is at:{0}'.format(c))
label_map[loc0]['xpos']=c[0]
label_map[loc0]['ypos']=c[1]
label_map[loc0]['label']='{0}/{1}'.format(label_map[loc0]['label'],label_map[loc1]['label'])
print('\t updating label_map[{0}]:{1}'.format(loc0,label_map[loc0]))
ax.text(*c,label_map[loc0]['label'])
_xticks=np.arange(0,6,1)
_xticklabels=['BA','NA','RM','FI','MI','TO']
ax.set_xticks(_xticks)
ax.set_xticklabels(_xticklabels)
ax.set_ylim(0,1.05*np.max(levels))
plt.show()
这主要取决于创建字典label_map
,该字典将原始的“位置”(即(2,5)
)映射到“ xtick顺序”(即(4,5)
)。在每个步骤i
中使用mk_fork()
创建一个“叉子”,它返回points
(随后在折线图中连接)以及connector
点,然后将其存储为'xpos','ypos'
中label_map
的新值。
我添加了多个print()
语句以强调每个步骤发生的情况,并添加了.text()
以突出显示每个“连接器”的位置。