Matplotlib:绘制长度不同的第二Y轴及相关格式问题
双Y轴Matplotlib图表格式优化方案
问题描述
我正在尝试使用Matplotlib绘制带有第二个Y轴的图表,目前已实现双Y轴功能,但想了解是否可以缩短第二个Y轴的长度。此外还存在以下格式设置难题:
- a) 希望在第二个Y轴上绘制箭头,样式与第一个Y轴的箭头一致;
- b) 将第二个Y轴对齐在-1位置,使其与X轴的交点为(…; -1);
- c) 当前X轴在原点处穿过X轴和Y轴刻度,需要避免该情况;
- d) 如何为两个Y轴设置统一的图例?
当前代码片段:
import matplotlib.pyplot as plt import numpy as np fig, ax = plt.subplots() bx = ax.twinx() # 2nd y-axis ax.spines['bottom'].set_position(('data',0)) ax.spines['left'].set_position(('data',0)) ax.xaxis.set_ticks_position('bottom') bx.spines['left'].set_position(('data',-1)) bx.spines['bottom'].set_position(('data',-1)) ax.spines["top"].set_visible(False) ax.spines["right"].set_visible(False) bx.spines["top"].set_visible(False) bx.spines["bottom"].set_visible(False) bx.spines["left"].set_visible(False) ## Graphs x_val = np.arange(0,10) y_val = 0.1*x_val ax.plot(x_val, y_val, 'k--') bx.plot(x_val, -y_val+1, color = 'purple') ## Arrows ms=2 #ax.plot(1, 0, ">k", ms=ms, transform=ax.get_yaxis_transform(), clip_on=False) ax.plot(0, 1, "^k", ms=ms, transform=ax.get_xaxis_transform(), clip_on=False) bx.plot(1, 1, "^k", ms=ms, transform=bx.get_xaxis_transform(), clip_on=False) plt.ylim((-1, 1.2)) bx.set_yticks([-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5]) ## Legend ax.legend([r'$A_{hull}$'], frameon=False, loc='upper left', bbox_to_anchor=(0.2, .75)) plt.show()
解决方案
1. 缩短第二个Y轴长度
直接通过bx.set_ylim()限制第二个Y轴的显示范围即可,轴的长度会自动适配设定的刻度区间。例如要让Y轴仅显示-1到0.5的范围,添加bx.set_ylim(-1, 0.5)即可。
2. 格式设置难题逐个解决
a) 为第二个Y轴添加一致样式的箭头
第一个Y轴的箭头是基于轴坐标绘制的,第二个Y轴需要对应调整变换方式:
- 第二个Y轴是
twinx()生成的右侧轴,用bx.get_yaxis_transform()确保坐标相对Y轴,在轴顶端绘制^k箭头即可和左侧轴样式统一。
b) 对齐第二个Y轴到-1位置
当前代码错误设置了左侧脊柱的位置,第二个Y轴的显示脊柱是右侧,修改为:
bx.spines['right'].set_position(('data', -1))
这样右侧Y轴会在X=-1的位置与X轴相交。
c) 避免X轴在原点穿过刻度
可以通过调整刻度内边距避免轴遮挡刻度,同时保留X轴在Y=0的位置:
ax.tick_params(axis='both', pad=10) bx.tick_params(axis='y', pad=10)
如果不需要X轴在Y=0位置,也可以将X轴脊柱设为轴底部:ax.spines['bottom'].set_position('axes')。
d) 统一两个Y轴的图例
先保存两个轴的线条对象,再基于整个画布创建统一图例:
# 保存线条对象 line1, = ax.plot(x_val, y_val, 'k--', label=r'$A_{hull}$') line2, = bx.plot(x_val, -y_val+1, color='purple', label='Second Series') # 统一生成图例 fig.legend([line1, line2], [line1.get_label(), line2.get_label()], frameon=False, loc='upper left', bbox_to_anchor=(0.2, .75))
修改后的完整代码
import matplotlib.pyplot as plt import numpy as np fig, ax = plt.subplots() bx = ax.twinx() # 2nd y-axis # 调整第一个Y轴和X轴的脊柱位置 ax.spines['bottom'].set_position(('data', 0)) ax.spines['left'].set_position(('data', 0)) ax.xaxis.set_ticks_position('bottom') # 调整第二个Y轴的脊柱位置到X=-1处 bx.spines['right'].set_position(('data', -1)) # 隐藏不需要的脊柱 ax.spines["top"].set_visible(False) ax.spines["right"].set_visible(False) bx.spines["top"].set_visible(False) bx.spines["bottom"].set_visible(False) bx.spines["left"].set_visible(False) # 绘制曲线并保存线条对象 x_val = np.arange(0,10) y_val = 0.1*x_val line1, = ax.plot(x_val, y_val, 'k--', label=r'$A_{hull}$') line2, = bx.plot(x_val, -y_val+1, color='purple', label='Second Series') # 添加轴箭头 ms = 5 # 第一个Y轴箭头(左侧Y轴顶端) ax.plot(0, 1, "^k", ms=ms, transform=ax.get_yaxis_transform(), clip_on=False) # 第一个X轴箭头(底部X轴右端) ax.plot(1, 0, ">k", ms=ms, transform=ax.get_xaxis_transform(), clip_on=False) # 第二个Y轴箭头(右侧Y轴顶端) bx.plot(1, 1, "^k", ms=ms, transform=bx.get_yaxis_transform(), clip_on=False) # 设置轴范围和刻度 ax.set_ylim((-1, 1.2)) bx.set_ylim(-1, 0.5) # 缩短第二个Y轴长度 bx.set_yticks([-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5]) # 调整刻度内边距避免被轴遮挡 ax.tick_params(axis='both', pad=10) bx.tick_params(axis='y', pad=10) # 统一图例 fig.legend([line1, line2], [line1.get_label(), line2.get_label()], frameon=False, loc='upper left', bbox_to_anchor=(0.2, .75)) plt.show()
内容的提问来源于stack exchange,提问作者Phtagen
相关产品推荐
相关产品推荐

