Matplotlib双Y轴分组标签导致热力图截断问题排查
问题描述
尝试为热力图的Y轴标签添加分组,通过第二个Y轴实现该功能,但添加后热力图上下部分被截断,图表边框也发生异常;移除第二个Y轴相关代码后,热力图显示正常。怀疑par2 = ax.twinx()存在问题,但使用ax.twiny()效果更差,询问错误原因及解决方法。
问题分析
核心问题出在两个方面:
twinx()创建的双Y轴会与原轴共享X轴,你将par2的左侧spine移到了图表外部(("axes", -0.3)),但fig.tight_layout()在计算布局时未考虑这个额外的左侧空间,导致热力图被挤压截断。- 未正确处理imshow默认的
origin='upper'设置:原Y轴的范围是(6.5, -0.5)(从上到下对应索引0到6),但你设置的par2刻度位置[-0.5, 3.5, 6.5]与这个范围方向相反,导致分组标签位置错误。
修复后的代码
import numpy as np import matplotlib.pyplot as plt import matplotlib.ticker as ticker vegetables = ["cucumber", "tomato", "lettuce", "asparagus", "potato", "wheat", "barley"] farmers = ["Farmer Joe", "Upland Bros.", "Smith Gardening", "Agrifun", "Organiculture", "BioGoods Ltd.", "Cornylee Corp."] harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0], [2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0], [1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0], [0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0], [0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0], [1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1], [0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]]) # 调整子图左边距,为分组标签留出空间 fig, ax = plt.subplots(figsize=(8,6)) fig.subplots_adjust(left=0.25) im = ax.imshow(harvest) # 旋转X轴标签 plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor") # 添加单元格注释 for i in range(len(vegetables)): for j in range(len(farmers)): text = ax.text(j, i, harvest[i, j], ha="center", va="center", color="w") ax.set_xticks(np.arange(len(farmers)), labels=farmers) ax.set_yticks(np.arange(len(vegetables)), labels=vegetables) # 添加第二个Y轴用于分组 par2 = ax.twinx() # 匹配原轴的Y范围(因origin='upper',范围是(6.5, -0.5)) par2.set_ylim(ax.get_ylim()) # 移动左侧spine到图表外,调整位置参数适配留出的左边距 par2.spines["left"].set_position(("axes", -0.2)) # 隐藏par2不需要的spine,避免干扰原图表边框 par2.spines["right"].set_visible(False) par2.spines["top"].set_visible(False) par2.spines["bottom"].set_visible(False) # 设置刻度样式 par2.tick_params('both', length=0, width=0, which='minor') par2.tick_params('both', direction='in', which='major') par2.yaxis.tick_left() par2.yaxis.set_label_position("left") par2.spines["left"].set_visible(True) # 设置分组相关刻度 par2.yaxis.set_major_formatter(ticker.NullFormatter()) # 匹配原轴的方向,设置正确的刻度位置 par2.set_yticks([6.5, 3.5, -0.5]) # 分组标签的位置对应分组的中间点 par2.yaxis.set_minor_locator(ticker.FixedLocator([4.5, 1.5])) par2.yaxis.set_minor_formatter(ticker.FixedFormatter(['tasty', 'not tasty'])) ax.set_title("Harvest of local farmers (in tons/year)") plt.show()
关键修复点
- 通过
fig.subplots_adjust(left=0.25)手动调整左侧边距,为分组标签预留空间,避免tight_layout挤压图表。 - 隐藏par2的右侧、顶部、底部spine,防止破坏原图表的边框样式。
- 根据imshow默认的
origin='upper',调整par2的刻度位置,确保分组标签显示在正确的分组中间位置。 - 调整par2的spine偏移量,适配预留的左边距,避免标签被截断。
内容的提问来源于stack exchange,提问作者Spynx
相关产品推荐
相关产品推荐

