Python使用Matplotlib绘制带填充图案的百分比堆叠柱状图
针对黑白印刷场景下分类区分度低的问题,详解利用Python Matplotlib绘制百分比堆叠柱状图的方法。通过动态计算占比、分配独立填充图案,配合白底黑框图例技巧,输出高对比度可视化效果。含完整代码、坐标轴格式化参数及排版优化指南,适用于科研绘图。
背景与核心思路
在需要黑白打印机输出或屏幕反色阅读的场景中,依赖RGB色值区分的图表往往会丢失分类特征。百分比堆叠柱状图能够直观反映结构占比变化,而引入字符填充图案(Hatch)可在不增加色彩依赖的前提下显著提升视觉边界感。由于Matplotlib原生legend()方法对图案属性的渲染支持有限,通常需要通过构造独立补丁对象(Patch)进行曲线救国式替换,从而实现图案与图例文字的精准绑定。

实现步骤与完整代码
第1步:初始化画布与数据预处理
导入numpy处理数值计算,调用matplotlib.pyplot与ticker模块。将原始数值阵列转换为二维数组后,按行求和得到每年的基数,随后初始化全零数组作为柱体堆叠的起始底边(bottom_y)。此阶段同时预定义好年份标签与各分区对应的填充字符集。
第2步:循环绘制分层柱体并锁定底部偏移
使用索引遍历数据分区。核心逻辑是将原始数值除以总和得出相对比例(y = data / sums),再调用ax.bar()绘制当前层。务必将上一层累积后的bottom_y传入bottom参数,并在每次迭代后执行bottom_y += y完成高度累加。此操作确保每个年份的柱体严格从零点向上堆叠,且各区间比例精确归一化至0~1范围。
第3步:格式化坐标轴与添加参考网格线
通过ax.yaxis.set_major_formatter(PercentFormatter(1))将纵轴刻度自动转为百分比样式。为方便读者快速定位阈值,可利用axhline()循环绘制间隔10%的水平虚线。同时清理冗余的顶部与右侧边框,使版面更符合学术出版规范。
第4步:生成兼容黑白打印的自定义图例
鉴于直接传递hatch参数到图例常出现渲染异常或留白,推荐采用matplotlib.patches.Patch手动构建句柄。设置facecolor='white'搭配edgecolor='black'保证打印清晰度,并将占位符图案符号嵌入标签文本中。通过bbox_to_anchor微调锚点位置,避免遮挡主绘图区。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
from matplotlib.ticker import PercentFormatter
import matplotlib.patches as mpatches
# 1. 定义数据与年份标签
labels = [str(i) for i in range(2010, 2021)]
data = [
[42.85, 41.15, 39.41, 35.35, 35.53, 30.45, 29.81, 31.85, 32.41, 30.42, 31.49],
[23.20, 26.40, 27.77, 29.02, 32.30, 35.40, 36.42, 35.95, 35.45, 34.00, 31.93],
[14.08, 12.99, 12.51, 11.54, 11.70, 12.27, 12.69, 11.81, 10.63, 9.98, 9.95],
[16.14, 16.17, 17.34, 21.53, 17.66, 19.36, 18.40, 17.83, 19.15, 23.09, 24.10],
[3.73, 3.28, 2.98, 2.57, 2.81, 2.53, 2.67, 2.57, 2.36, 2.51, 2.54]
]
marks = ['o', '/', '*', '..', '\\\\'] # 对应五个分区的填充字符
x = range(len(labels))
data_array = np.array(data)
sums = np.sum(data_array, axis=0) # 每年总计用于归一化
bottom_y = np.zeros(len(labels))
# 2. 创建画布与子图
fig, ax = plt.subplots(figsize=(8, 6))
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示
# 3. 逐层绘制柱体并计算相对比例
for j in range(len(data)):
y_pct = data_array[j] / sums
ax.bar(x, y_pct, width=0.6, hatch=marks[j], bottom=bottom_y,
color='white', edgecolor='black', linewidth=1.2)
bottom_y += y_pct
# 4. 格式化Y轴为百分比,并添加水平参考线
ax.yaxis.set_major_formatter(PercentFormatter(1))
for i in range(1, 11):
ax.axhline(y=i/10, linestyle='--', color='gray', linewidth=0.8)
# 5. 配置坐标轴标签与字体
ax.set_xticks(x)
ax.set_xticklabels(labels)
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_fontname('Times New Roman')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# 6. 曲线救国法生成图例:因Hatch在Legend中渲染受限,统一使用白底黑框+文字标注
legend_handles = [mpatches.Patch(color='white', edgecolor='black', label=f'类别{chr(65+h)} ({marks[h]})') for h in range(len(data))]
ax.legend(handles=legend_handles, ncol=1, loc='upper left', bbox_to_anchor=(1.05, 1), frameon=True)
# 调整布局并导出
fig.subplots_adjust(right=0.7)
plt.savefig('stacked_percentage_hatch.svg', format='svg', dpi=300, bbox_inches='tight')
plt.show()结果验证与注意事项
运行脚本后,控制台将弹出图形窗口,后台同步输出矢量SVG文件。打开图像可确认以下特征是否符合预期:
- 纵轴刻度是否完整显示为
0%、20%…100%且无科学计数法干扰。 - 每个年份上方的总高度是否严格对齐
100%基准线。 - 图例方块边缘是否清晰,未出现透明重叠导致的视觉粘连。
若执行时报错KeyError: 'SimHei',说明系统缺乏对应中文字体。可将其替换为'Microsoft YaHei'或安装开源字体。此外,原草稿逻辑将图例绘制嵌套在数据循环内部,会导致画布反复重绘并触发性能警告;本版本已将其移至外部批量处理,兼顾了执行效率与内存安全。导出长宽比失调时,请检查subplots_adjust()的right参数是否被右侧图例挤压。
总结
以上就是Python绘制百分比堆叠柱状图并填充图案的详细内容,更多关于Matplotlib高级排版与学术级数据可视化的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













