Python中如何在Matplotlib子图间添加跨子图连接线?
问题描述
我现有单图绘制代码(可添加竖线):
mid = d[d.Position==0].Price.mean() b_a = d[(d.Position==0) & (d.Side == 0)].Price.values b_b = d[(d.Position==0) & (d.Side == 1)].Price.values f, ax = plt.subplots() sns.set_color_codes('muted') sns.barplot(data = d[d.Side==0], x = 'Price', y = 'Size', color = 'b', native_scale=True) sns.barplot(data = d[d.Side==1], x = 'Price', y = 'Size', color = 'r', native_scale=True) ax.xaxis.set_major_locator(ticker.MultipleLocator(.0001)) plt.axvline(x=mid, color = 'b', lw = 1.5) plt.axvline(x=b_a, color = 'k', lw = 1, ls='--') plt.axvline(x=b_b, color = 'k', lw = 1, ls='--')
数据示例:
Position Operation Side Price Size 9 9 0 1 0.7289 -16 8 8 0 1 0.729 -427 7 7 0 1 0.7291 -267 6 6 0 1 0.7292 -15 5 5 0 1 0.7293 -16 4 4 0 1 0.7294 -16 3 3 0 1 0.7295 -426 2 2 0 1 0.7296 -8 1 1 0 1 0.7297 -14 0 0 0 1 0.7298 -37 10 0 0 0 0.7299 6 11 1 0 0 0.73 34 12 2 0 0 0.7301 7 13 3 0 0 0.7302 9 14 4 0 0 0.7303 16 15 5 0 0 0.7304 15 16 6 0 0 0.7305 429 17 7 0 0 0.7306 16 18 8 0 0 0.7307 265 19 9 0 0 0.7308 18
我计划用Matplotlib的subplots绘制多个此类子图,代码如下:
cnt = 5 f, ax = plt.subplots(cnt, 1, sharex=True) sns.set_color_codes('muted') for i in range(cnt): sns.barplot(x = x.iloc[i, 10:].values, y = y.iloc[i, 10:].values, color = 'b', native_scale=True, ax = ax[i]) sns.barplot(x = x.iloc[i, :10].values, y = y.iloc[i, 10:].values, color = 'r', native_scale=True, ax = ax[i]) ax[i].xaxis.set_major_locator(ticker.MultipleLocator(.0001))
请问是否可以在这些子图之间添加连接线,类似示例图中的效果?
解决方案
完全可以在子图之间添加连接线。核心思路是利用Matplotlib的transFigure画布坐标系,将子图内的关键竖线端点坐标转换为画布坐标,再在相邻子图的对应端点间绘制线段。
结合你的代码,修改后的实现如下:
import matplotlib.pyplot as plt import seaborn as sns import matplotlib.ticker as ticker cnt = 5 # 预计算每个子图的关键竖线位置(实际使用时替换为你的真实计算逻辑) mid_list = [0.7299 + i*0.0001 for i in range(cnt)] b_a_list = [0.73 + i*0.0001 for i in range(cnt)] b_b_list = [0.7298 + i*0.0001 for i in range(cnt)] f, ax = plt.subplots(cnt, 1, sharex=True, figsize=(8, 10)) sns.set_color_codes('muted') # 先绘制所有子图和内部竖线,同时记录关键端点坐标 for i in range(cnt): # 绘制买卖盘柱状图 sns.barplot(x=x.iloc[i, 10:].values, y=y.iloc[i, 10:].values, color='b', native_scale=True, ax=ax[i]) sns.barplot(x=x.iloc[i, :10].values, y=y.iloc[i, 10:].values, color='r', native_scale=True, ax=ax[i]) ax[i].xaxis.set_major_locator(ticker.MultipleLocator(.0001)) # 绘制子图内的竖线 ax[i].axvline(x=mid_list[i], color='b', lw=1.5) ax[i].axvline(x=b_a_list[i], color='k', lw=1, ls='--') ax[i].axvline(x=b_b_list[i], color='k', lw=1, ls='--') # 转换坐标:子图数据坐标 → 画布坐标 y_top = ax[i].get_ylim()[1] y_bottom = ax[i].get_ylim()[0] mid_pos_top = ax[i].transData.transform((mid_list[i], y_top)) b_a_pos_top = ax[i].transData.transform((b_a_list[i], y_top)) b_b_pos_top = ax[i].transData.transform((b_b_list[i], y_top)) mid_pos_bottom = ax[i].transData.transform((mid_list[i], y_bottom)) b_a_pos_bottom = ax[i].transData.transform((b_a_list[i], y_bottom)) b_b_pos_bottom = ax[i].transData.transform((b_b_list[i], y_bottom)) # 相邻子图间连线 if i > 0: # 连接mid线 line_mid = plt.Line2D([prev_mid_top[0], mid_pos_bottom[0]], [prev_mid_top[1], mid_pos_bottom[1]], color='b', lw=1.5, transform=f.transFigure) f.add_artist(line_mid) # 连接b_a线 line_a = plt.Line2D([prev_b_a_top[0], b_a_pos_bottom[0]], [prev_b_a_top[1], b_a_pos_bottom[1]], color='k', lw=1, ls='--', transform=f.transFigure) f.add_artist(line_a) # 连接b_b线 line_b = plt.Line2D([prev_b_b_top[0], b_b_pos_bottom[0]], [prev_b_b_top[1], b_b_pos_bottom[1]], color='k', lw=1, ls='--', transform=f.transFigure) f.add_artist(line_b) # 更新上一个子图的顶部坐标 prev_mid_top = mid_pos_top prev_b_a_top = b_a_pos_top prev_b_b_top = b_b_pos_top plt.tight_layout() plt.show()
关键说明
- 使用
transData.transform()将子图内的数据坐标转换为画布坐标,避免因子图布局变化导致连线错位。 - 每个子图绘制完成后,记录关键竖线的上下端点,相邻子图的对应端点用
Line2D连接,并添加到画布对象中。 - 实际使用时,需将模拟的
mid_list、b_a_list、b_b_list替换为你针对每个子图计算的真实竖线位置。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

