用Matplotlib轻松绘制树状图:从原理到实战,一文搞定层级数据可视化
在数据可视化中,树状图(Hierarchical Tree)是呈现层级关系数据的“利器”——小到文件目录结构,大到生物进化谱系、用户分群聚类,都能通过它清晰展现数据的从属与相似关系。今天就带大家从零开始,用Matplotlib实现树状图绘制,还会拆解关键参数与实战技巧,让你看完就能上手。
一、先搞懂:树状图的核心价值与适用场景
在写代码前,我们得先明确“什么时候该用树状图”。它的核心优势是将抽象的层级/聚类关系转化为直观的分支结构,尤其适合这三类场景:
1. 聚类结果展示:比如K-Means聚类后,用树状图呈现不同样本的相似程度(分支越近,相似度越高);
2. 层级数据梳理:像公司组织架构、产品分类体系、论文参考文献层级等;
3. 谱系关系呈现:例如生物分类(界-门-纲-目-科-属-种)、家族族谱等。
如果你的数据包含“父-子”从属关系,或需要体现“相似性聚类”,树状图会比普通图表更有说服力。
二、技术准备:Matplotlib绘制树状图的“关键依赖”
Matplotlib本身没有直接绘制树状图的API,但可以借助 scipy.cluster.hierarchy 工具包——它能先对数据进行层级聚类计算,再生成可被Matplotlib渲染的树状图结构。所以第一步,先安装依赖包:
pip install matplotlib scipy numpy pandas
核心库分工:
scipy.cluster.hierarchy :负责层级聚类计算与树状图结构生成;
matplotlib.pyplot :负责最终的图表渲染、样式调整;
numpy/pandas :负责数据预处理(如生成模拟数据或读取真实数据)。
三、实战1:基础树状图绘制——从0到1生成第一个图
我们先从最简单的“模拟数据”入手,绘制一个基础树状图。比如模拟10个样本的聚类结果,用树状图展示它们的相似关系。
步骤1:生成模拟数据与聚类计算
首先用 numpy 生成10个样本(每个样本含2个特征),再通过 linkage 函数计算样本间的“距离”(即聚类依据):
import matplotlib.pyplot as plt
import numpy as np
from scipy.cluster.hierarchy import linkage, dendrogram
# 1. 生成模拟数据:10个样本,每个样本2个特征
np.random.seed(42) # 固定随机种子,保证结果可复现
data = np.random.rand(10, 2) # 10行2列的随机数据,值在0-1之间
# 2. 计算层级聚类的“链接矩阵”:用ward方法(最小化类内方差)计算样本距离
# linkage函数是核心:输入数据、聚类方法,输出聚类结果矩阵
linkage_matrix = linkage(data, method='ward')
这里的 method='ward' 是常用的聚类方法,适合大多数场景;如果需要其他方法,也可以选 'single' (最短距离)、 'complete' (最长距离)等。
步骤2:用Matplotlib渲染树状图
有了 linkage_matrix ,再用 dendrogram 函数生成树状图结构,最后用Matplotlib调整样式并展示:
# 3. 创建画布与子图
plt.figure(figsize=(10, 6)) # 画布大小:宽10英寸,高6英寸
# 4. 绘制树状图:dendrogram函数将linkage矩阵转化为树状图
dendrogram(
linkage_matrix,
labels=[f"样本{i+1}" for i in range(10)], # 给每个样本添加标签(避免默认数字)
leaf_rotation=45, # 叶子标签旋转45度,防止重叠
leaf_font_size=10, # 叶子标签字体大小
color_threshold=5, # 颜色阈值:距离小于5的分支用同色,大于5的用不同色(区分聚类)
above_threshold_color='gray' # 超过阈值的分支颜色(灰色)
)
# 5. 调整图表样式
plt.title('基础树状图:10个样本的层级聚类结果', fontsize=14, pad=20) # 标题与边距
plt.xlabel('样本名称', fontsize=12) # X轴标签
plt.ylabel('聚类距离(类内方差)', fontsize=12) # Y轴标签:ward方法下是“类内方差”
plt.tight_layout() # 自动调整布局,防止标签被截断
# 6. 展示或保存图表
plt.show()
# plt.savefig('基础树状图.png', dpi=300, bbox_inches='tight') # 保存为高清图片(dpi=300)

结果解读:
横向分支:每个“Y”型分支代表一次聚类(两个样本/小类合并为一个大类);
Y轴高度:代表聚类时的“距离”(ward方法下,高度越高,合并的两类相似度越低);
颜色:颜色相同的分支属于同一聚类(由 color_threshold 控制,这里小于5的为同色,共3类);
叶子标签:底部的“样本1-样本10”是原始样本,旋转45度后避免了标签重叠。
四、实战2:进阶优化——让树状图更“专业”
基础图能看,但实际工作中需要更精细的样式调整。比如给分支添加数值标签、调整颜色方案、适配中文显示等。
进阶代码:带数值标签与中文优化的树状图
import matplotlib.pyplot as plt
import numpy as np
from scipy.cluster.hierarchy import linkage, dendrogram
# 1. 解决Matplotlib中文显示问题(关键!避免中文乱码)
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei'] # 适配Linux
# plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 适配Windows
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 2. 用真实场景数据:比如5个产品的“销量-好评率”数据
products = ['产品A', '产品B', '产品C', '产品D', '产品E']
sales = [85, 62, 93, 78, 55] # 销量(百件)
rating = [4.8, 4.2, 4.9, 4.5, 3.9] # 好评率(分)
data = np.column_stack((sales, rating)) # 合并为2列数据
# 3. 计算聚类矩阵(用complete方法:基于最长距离)
linkage_matrix = linkage(data, method='complete')
# 4. 绘制树状图,并重定义分支样式
plt.figure(figsize=(10, 6))
# 核心:用dendrogram返回值获取分支位置,添加数值标签
dend = dendrogram(
linkage_matrix,
labels=products, # 用产品名作为标签
leaf_rotation=0, # 产品名较短,不旋转
leaf_font_size=11,
color_threshold=15, # 调整颜色阈值,区分2个大类
above_threshold_color='lightgray',
linewidths=1.5 # 加粗分支线条
)
# 给每个分支添加“聚类距离”标签(关键进阶操作)
for i, d, c in zip(dend['icoord'], dend['dcoord'], dend['color_list']):
x = 0.5 * sum(i[1:3]) # 分支中点的X坐标
y = d[1] # 分支的Y坐标(即聚类距离)
plt.plot(x, y, 'o', color=c, markersize=6) # 画一个小圆点
plt.annotate(
f'{y:.1f}', # 显示距离数值(保留1位小数)
xy=(x, y),
xytext=(0, 5), # 文本在点上方5个像素
textcoords='offset points',
ha='center', # 水平居中
fontsize=9
)
# 5. 最终样式调整
plt.title('产品层级聚类树状图(销量-好评率)', fontsize=14, pad=20, fontweight='bold')
plt.xlabel('产品名称', fontsize=12)
plt.ylabel('聚类距离(最长距离法)', fontsize=12)
plt.grid(axis='y', alpha=0.3, linestyle='--') # 添加Y轴网格线,方便读值
plt.tight_layout()
plt.show()

进阶亮点:
1. 中文适配:通过 plt.rcParams 设置中文字体,解决乱码问题;
2. 数值标签:用 dendrogram 返回的 icoord (X坐标)和 dcoord (Y坐标),给每个分支添加“聚类距离”标签,让读者直观看到合并时的距离;
3. 网格线:添加Y轴网格线,方便对比不同分支的距离大小;
4. 真实数据:用“产品销量-好评率”数据,更贴近实际工作场景。
五、实战3:树状图+热力图——强强联合,呈现多维度数据
之前我们提到,树状图适合展示层级关系,而热力图适合展示数值大小。两者结合能让数据更立体——比如分析10个城市的“气温、湿度、PM2.5”数据,用树状图聚类城市,用热力图展示指标数值。
联合可视化代码:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.cluster.hierarchy import linkage, dendrogram, leaves_list
# 1. 中文适配与数据准备
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False
# 模拟10个城市的3个环境指标数据
cities = ['北京', '上海', '广州', '深圳', '杭州', '成都', '武汉', '西安', '南京', '重庆']
np.random.seed(42)
data = np.random.randint(30, 90, size=(10, 3)) # 10行3列:10个城市,3个指标
df = pd.DataFrame(data, index=cities, columns=['气温(℃)', '湿度(%)', 'PM2.5(μg/m³)'])
# 2. 计算城市聚类的linkage矩阵(按行聚类)
linkage_matrix = linkage(df, method='ward')
# 获取聚类后的城市顺序(用于热力图与树状图对齐)
leaf_order = leaves_list(linkage_matrix)
df_ordered = df.iloc[leaf_order] # 按聚类顺序重新排列DataFrame
# 3. 创建2x1的子图布局:上方树状图,下方热力图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10), gridspec_kw={'height_ratios': [1, 2]})
# 4. 绘制上方树状图(隐藏X轴,与热力图对齐)
dendrogram(
linkage_matrix,
labels=df.index,
ax=ax1,
leaf_rotation=45,
leaf_font_size=10,
color_threshold=80,
above_threshold_color='gray'
)
ax1.set_xlabel('') # 隐藏树状图的X轴(与热力图X轴重复)
ax1.set_title('城市环境指标层级聚类:树状图+热力图', fontsize=14, pad=20, fontweight='bold')
# 5. 绘制下方热力图(按聚类顺序排列)
im = ax2.imshow(df_ordered.values, cmap='YlOrRd', aspect='auto') # YlOrRd:黄-橙-红配色
# 6. 调整热力图标签
ax2.set_xticks(range(len(df.columns)))
ax2.set_xticklabels(df.columns, fontsize=11)
ax2.set_yticks(range(len(df_ordered.index)))
ax2.set_yticklabels(df_ordered.index, fontsize=10)
# 7. 给热力图每个格子添加数值标签
for i in range(len(df_ordered.index)):
for j in range(len(df.columns)):
text = ax2.text(j, i, df_ordered.iloc[i, j], ha='center', va='center', fontsize=9)
# 8. 添加颜色条(解释热力图颜色含义)
cbar = plt.colorbar(im, ax=ax2, shrink=0.8)
cbar.set_label('指标数值', fontsize=11)
# 9. 调整布局
plt.tight_layout()
plt.show()

联合优势:
树状图:展示10个城市的聚类关系(比如“北京-西安”相似度高,“广州-深圳”相似度高);
热力图:按聚类顺序排列城市,用颜色深浅展示“气温、湿度、PM2.5”的具体数值,让“为什么这两个城市聚类在一起”有了数据支撑(比如北京和西安的PM2.5数值都偏高)。
六、避坑指南:新手常犯的3个错误
1. 中文乱码:忘记设置中文字体,导致标签显示为“方框”。解决方法:必加 plt.rcParams 字体设置;
2. 标签重叠:样本/类别名称过长,导致底部标签重叠。解决方法:用 leaf_rotation=45 旋转标签,或缩小 leaf_font_size ;
3. 颜色阈值不合理: color_threshold 设置过大/过小,导致聚类颜色区分不明显。解决方法:先看Y轴最大距离,再将阈值设为“最大距离的1/2~2/3”(比如Y轴最大距离100,阈值设为50~60)。
七、总结
Matplotlib+Scipy的组合,能轻松实现从“基础树状图”到“进阶联合可视化”的全流程。核心步骤可以概括为:
1. 用 scipy.cluster.hierarchy.linkage 计算聚类矩阵;
2. 用 dendrogram 生成树状图结构;
3. 用Matplotlib调整样式(标签、颜色、布局);
4. 按需与热力图、散点图组合,提升数据解读力。
无论是汇报聚类结果,还是梳理层级数据,树状图都是你的“可视化加分项”。赶紧用自己的数据试试吧!
更多推荐
所有评论(0)