0

0

Matplotlib中利用单轴实现多类别水平条形图的精细化布局

霞舞

霞舞

发布时间:2025-08-11 21:24:16

|

253人浏览过

|

来源于php中文网

原创

matplotlib中利用单轴实现多类别水平条形图的精细化布局

本文深入探讨了在Matplotlib中创建具有相邻视觉效果的多类别水平条形图的技巧。针对传统多子图方法在间距控制和图例整合上的挑战,我们提出了一种高效的单轴解决方案。通过巧妙结合plt.barh、plt.axhline和plt.text,本教程将指导您如何精确控制条形图间距、实现清晰的类别分隔,并统一管理图例,最终生成专业且可读性强的可视化图表。

1. 引言与挑战

在数据可视化中,水平条形图(barh)常用于比较不同类别或项目的值。当需要在一个图表中展示多个相关但又需明确区分的类别组时,例如本例中“Alcohol”和“Formalin”两种存储协议下的数据,通常会考虑使用Matplotlib的子图(subplots)功能。然而,直接使用plt.subplots(2, 1, sharex=True)并尝试通过fig.subplots_adjust(hspace=0)来消除子图间距时,往往难以精确控制不同类别组内部条形与组间条形的相对间距,且在图例合并管理上也可能遇到挑战。例如,不同类别组的条形可能需要不同的高度或内部间距,这在分离的子图中实现起来较为繁琐,并且统一的图例也需要额外处理。

2. 优化方案:单轴结合辅助元素

为了克服上述挑战,一种更为灵活和精确的方案是:利用单个坐标轴绘制所有条形,并通过辅助绘图元素(如水平线plt.axhline和文本plt.text)来模拟类别分隔和标签。 这种方法允许我们对所有条形的y轴位置进行统一规划和精细控制,从而实现更灵活的布局和视觉效果。

2.1 核心思想

  1. 统一Y轴坐标系: 将所有条形(无论属于哪个类别)放置在同一个Y轴上,通过精心设计的Y坐标值来控制它们的位置和相互间距。
  2. 类别分隔线: 使用plt.axhline在不同类别组之间绘制一条水平线,作为视觉上的分隔符。
  3. 类别标签: 使用plt.text在相应类别组的旁边添加文本标签,明确指示每个组的名称。
  4. 图例管理: 由于所有条形都在一个轴上,图例的生成和管理变得更加直接。可以利用Matplotlib的图例机制,或者通过在label前添加下划线_来排除某些条形不显示在图例中。

3. 实现步骤与示例代码

下面我们将通过一个完整的示例来展示如何使用这种方法来重现目标图表。

聚蜂消防BeesFPD
聚蜂消防BeesFPD

关注消防领域的智慧云平台

下载
import matplotlib.pyplot as plt
import numpy as np

# 设置图表风格
plt.style.use('default')

# 准备数据
# Alcohol类别数据
labels_alcohol = ['Borchers et al.', 'Donnelly et al.']
values_alcohol = [24, 1]

# Formalin类别数据
labels_formalin = ['Wang and Feng', 'van Haaren et al.', 'Borchers et al.', 'Gustafson et al.', 'Ebacher et al.']
values_formalin = [3, 3, 24, 52, 52]

# 定义图例标签和对应的填充图案
legend_labels = {
    'No Change': '\\',
    'Major Change': 'x',
    'Minor Change': 'o'
}

# 创建一个新的图表和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))

# --- 绘制 Alcohol 类别条形图 ---
# 为Alcohol类别条形定义Y轴位置和高度
# 这里我们将Alcohol的条形放置在较低的Y轴位置,并设置较小的height以增加间距
y_pos_alcohol = np.arange(len(labels_alcohol)) * 1.2 # 增加间距
bar_height_alcohol = 0.8 # 调整条形高度

# 绘制Alcohol条形
# 注意:为了控制图例,我们只在第一个代表“No Change”的条形上设置label,
# 其他同类别的条形通过在label前加下划线`_`来避免重复显示在图例中
ax.barh(y_pos_alcohol[0], values_alcohol[0], color='white', label='No Change', hatch=legend_labels['No Change'], edgecolor="black", height=bar_height_alcohol)
ax.barh(y_pos_alcohol[1], values_alcohol[1], color='white', label='Major Change', hatch=legend_labels['Major Change'], edgecolor="black", height=bar_height_alcohol)

# --- 绘制 Formalin 类别条形图 ---
# 为Formalin类别条形定义Y轴位置和高度
# Formalin的条形从Alcohol条形上方开始,并设置较大的height使其更紧密
# 预留一些空间给分隔线和类别标签
y_offset_formalin = y_pos_alcohol[-1] + 2.5 # 从Alcohol最后一个条形上方开始,预留2.5单位空间
y_pos_formalin = y_offset_formalin + np.arange(len(labels_formalin)) * 1.0 # 增加间距,但比Alcohol小
bar_height_formalin = 0.9 # 调整条形高度

# 绘制Formalin条形
ax.barh(y_pos_formalin[0], values_formalin[0], color='white', label='_No Change', hatch=legend_labels['No Change'], edgecolor="black", height=bar_height_formalin)
ax.barh(y_pos_formalin[1], values_formalin[1], color='white', label='_Major Change', hatch=legend_labels['Major Change'], edgecolor="black", height=bar_height_formalin)
ax.barh(y_pos_formalin[2], values_formalin[2], color='white', label='Minor Change', hatch=legend_labels['Minor Change'], edgecolor="black", height=bar_height_formalin)
ax.barh(y_pos_formalin[3], values_formalin[3], color='white', label='_No Change', hatch=legend_labels['No Change'], edgecolor="black", height=bar_height_formalin)
ax.barh(y_pos_formalin[4], values_formalin[4], color='white', label='_No Change', hatch=legend_labels['No Change'], edgecolor="black", height=bar_height_formalin)


# --- 添加类别分隔线 ---
# y值选择在两个类别组的中间
separator_y = y_pos_alcohol[-1] + (y_offset_formalin - y_pos_alcohol[-1]) / 2
ax.axhline(y=separator_y, xmin=-0.15, xmax=1.05, color='black', linewidth=0.8, linestyle='-', clip_on=False)

# --- 添加类别标签 ---
# 计算每个类别组的中心Y轴位置
center_y_alcohol = np.mean(y_pos_alcohol)
center_y_formalin = np.mean(y_pos_formalin)

# x轴位置需要根据数据最大值和图表宽度进行调整,以确保文本在图表外侧且不被裁剪
# 假设最大X值为52,我们可以在X轴右侧留出一些空间
max_x_value = max(max(values_alcohol), max(values_formalin))
# 可以尝试将文本放置在略微超出X轴范围的位置,并使用clip_on=False确保显示
text_x_position = max_x_value + 3 # 调整这个值以获得最佳视觉效果

ax.text(x=text_x_position, y=center_y_alcohol, s='Alcohol', rotation='vertical', clip_on=False, fontsize='x-large', ha='left', va='center')
ax.text(x=text_x_position, y=center_y_formalin, s='Formalin', rotation='vertical', clip_on=False, fontsize='x-large', ha='left', va='center')


# --- 设置Y轴刻度标签 ---
# 合并所有标签和对应的Y轴位置
all_labels = labels_alcohol + labels_formalin
all_y_pos = list(y_pos_alcohol) + list(y_pos_formalin)

ax.set_yticks(all_y_pos)
ax.set_yticklabels(all_labels)
ax.set_ylabel('') # 清除Y轴标题,因为类别标签已通过plt.text添加

# 设置X轴标签
ax.set_xlabel('Weeks Stored')

# 设置X轴范围,留出一些右侧空间给类别标签
ax.set_xlim(0, max_x_value + 10) # 适当增加X轴最大值,给右侧文本留空间

# 显示图例
ax.legend()

# 调整布局,确保所有元素可见
plt.tight_layout()

# 保存和显示图表
plt.savefig("Storage Protocol Plot_Optimized.pdf")
plt.show()

4. 注意事项与技巧

  1. Y轴位置规划:
    • 为每个条形分配一个唯一的Y轴坐标。可以使用np.arange(n)作为基础,然后乘以一个因子(例如* 1.2或* 1.0)来调整条形之间的相对间距。
    • 在不同类别组之间,通过增加y_offset来创建明显的视觉间隔。
    • 确保所有Y轴标签(set_yticks和set_yticklabels)与条形的位置一一对应。
  2. 条形高度(height参数):
    • barh函数的height参数控制条形在Y轴上的厚度。其默认值为0.8。
    • 可以通过调整height来控制条形内部的紧密程度。例如,对于需要更稀疏布局的类别,可以设置较小的height值(如0.2);对于更紧密的布局,可以设置较大的height值(如0.9)。
  3. 图例管理:
    • plt.legend()会自动收集所有带有label参数的绘图元素的标签。
    • 如果某个条形代表的样式与已有的图例项相同,且您不希望它在图例中重复出现,可以在其label前添加一个下划线_(例如label='_No Change')。Matplotlib通常会忽略以_开头的标签。
    • 更高级的图例控制可以使用ax.get_legend_handles_labels()来手动筛选和创建图例。
  4. 分隔线(plt.axhline):
    • y参数指定水平线在Y轴上的位置。
    • xmin和xmax参数控制水平线在X轴上的起始和结束比例(0到1代表坐标轴的完整范围)。通过设置小于0或大于1的值,并结合clip_on=False,可以让线条超出坐标轴边界,达到更好的视觉效果。
    • clip_on=False:此参数非常重要,它允许绘图元素(如线条、文本)显示在坐标轴边界之外,对于在图表边缘添加辅助信息非常有用。
  5. 类别标签(plt.text):
    • x和y参数指定文本的坐标位置。
    • s参数是文本内容。
    • rotation='vertical'使文本垂直显示。
    • clip_on=False确保文本不会被裁剪。
    • ha (horizontal alignment) 和 va (vertical alignment) 参数用于控制文本的对齐方式,例如ha='left'和va='center'。
    • fontsize可以调整文本大小。
    • 文本的x位置需要根据X轴的最大值和图表布局进行调整,以确保其位于条形图的右侧并有足够的空间。
  6. X轴范围调整:
    • ax.set_xlim()可以手动设置X轴的显示范围。为了给右侧的垂直类别标签留出空间,需要将X轴的最大值适当增大。

5. 总结

通过将多个类别组的数据绘制在单个Matplotlib坐标轴上,并辅以plt.axhline和plt.text进行视觉分隔和标签,我们能够实现比传统多子图方法更精细、更灵活的水平条形图布局。这种方法不仅简化了图例的管理,也为图表的整体美观性和专业性提供了更强的控制力,特别适用于需要在一个统一视图中展示相关但又需明确区分的数据集场景。掌握这些技巧,将有助于您创建更具表现力和洞察力的数据可视化作品。

相关专题

更多
Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

0

2026.01.15

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

2

2026.01.15

公务员调剂条件 2026调剂公告时间
公务员调剂条件 2026调剂公告时间

(一)符合拟调剂职位所要求的资格条件。 (二)公共科目笔试成绩同时达到拟调剂职位和原报考职位的合格分数线,且考试类别相同。 拟调剂职位设置了专业科目笔试条件的,专业科目笔试成绩还须同时达到合格分数线,且考试类别相同。 (三)未进入原报考职位面试人员名单。

10

2026.01.15

国考成绩查询入口 国考分数公布时间2026
国考成绩查询入口 国考分数公布时间2026

笔试成绩查询入口已开通,考生可登录国家公务员局中央机关及其直属机构2026年度考试录用公务员专题网站http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/examResult/written_result.html,查询笔试成绩和合格分数线,点击“笔试成绩查询”按钮,凭借身份证及准考证进行查询。

2

2026.01.15

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

63

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

32

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

73

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

20

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

25

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
誉天教育RHCE视频教程
誉天教育RHCE视频教程

共9课时 | 1.4万人学习

尚观Linux RHCE视频教程(二)
尚观Linux RHCE视频教程(二)

共34课时 | 5.7万人学习

尚观RHCE视频教程(一)
尚观RHCE视频教程(一)

共28课时 | 4.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号