Matplotlib循环绘图:多ID数据同图实现断轴子图叠加
解决DataFrame多ID断轴图叠加在同一画布的问题
需求说明
我有一个包含多个ID的DataFrame,希望为每个ID绘制断轴图,且所有图形都叠加在同一个figure中,而非每个ID对应单独figure。当前示例代码生成多个独立figure,需要实现如下叠加效果。
示例数据与原代码
import pandas as pd import matplotlib.pyplot as plt df = pd.DataFrame({'id':['id1','id1','id1','id1','id1','id1','id1','id1','id1','id1', 'id2','id2','id2','id2','id2','id2','id2','id2','id2','id2'], 'x': [0, 1, 2, 3, 4, 5, 1000, 2000, 3000, 4000, 0, 1, 2, 3, 4, 5, 1000, 2000, 3000, 4000,], 'y': [5, 4, 5, 4, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 7, 7, 8, 7, 9, 5]}) def _custom_plot(frame): x = frame['x'] y = frame['y'] f,(ax,ax2) = plt.subplots(1,2,sharey=True, facecolor='w', figsize=(15, 5)) # plot the same data on both axes ax.plot(x, y, 'o--', color='grey', alpha=0.3) ax2.plot(x, y, 'o--', color='grey', alpha=0.3) ax.set_xlim(0,100) ax2.set_xlim(1e3,5e3) # hide the spines between ax and ax2 ax.spines['right'].set_visible(False) ax2.spines['left'].set_visible(False) ax.yaxis.tick_left() ax2.yaxis.tick_right() d = .015 # how big to make the diagonal lines in axes coordinates # arguments to pass plot, just so we don't keep repeating them kwargs = dict(transform=ax.transAxes, color='k', clip_on=False) ax.plot((1-d,1+d), (-d,+d), **kwargs) ax.plot((1-d,1+d),(1-d,1+d), **kwargs) kwargs.update(transform=ax2.transAxes) # switch to the bottom axes ax2.plot((-d,+d), (1-d,1+d), **kwargs) ax2.plot((-d,+d), (-d,+d), **kwargs) plt.yticks([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) plt.show() for id in df['id'].unique(): _custom_plot(df[df['id']==id])
当前效果

期望效果

解决方案
修改思路
- 提前创建全局的断轴画布(两个子轴),避免循环中重复生成新figure
- 让绘图函数接收已创建的轴对象,在指定轴上绘制每个ID的曲线
- 为不同ID设置差异化的样式(颜色、标记),提升辨识度
- 统一在最后配置断轴样式、轴范围和显示参数,减少重复操作
修改后的代码
import pandas as pd import matplotlib.pyplot as plt df = pd.DataFrame({'id':['id1','id1','id1','id1','id1','id1','id1','id1','id1','id1', 'id2','id2','id2','id2','id2','id2','id2','id2','id2','id2'], 'x': [0, 1, 2, 3, 4, 5, 1000, 2000, 3000, 4000, 0, 1, 2, 3, 4, 5, 1000, 2000, 3000, 4000,], 'y': [5, 4, 5, 4, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 7, 7, 8, 7, 9, 5]}) # 定义样式映射,区分不同ID style_map = { 'id1': ('o--', '#1f77b4'), 'id2': ('s-.', '#ff7f0e') } def _custom_plot(frame, ax, ax2, style): x = frame['x'] y = frame['y'] # 在已有的轴上绘图 ax.plot(x, y, style[0], color=style[1], alpha=0.8, label=frame['id'].iloc[0]) ax2.plot(x, y, style[0], color=style[1], alpha=0.8) # 提前创建全局画布和轴 f,(ax,ax2) = plt.subplots(1,2,sharey=True, facecolor='w', figsize=(15, 5)) # 循环每个ID绘图 for id in df['id'].unique(): subset = df[df['id']==id] _custom_plot(subset, ax, ax2, style_map[id]) # 统一设置轴范围 ax.set_xlim(0,100) ax2.set_xlim(1e3,5e3) # 隐藏中间的轴脊 ax.spines['right'].set_visible(False) ax2.spines['left'].set_visible(False) ax.yaxis.tick_left() ax2.yaxis.tick_right() # 添加断轴斜线 d = .015 kwargs = dict(transform=ax.transAxes, color='k', clip_on=False) ax.plot((1-d,1+d), (-d,+d), **kwargs) ax.plot((1-d,1+d),(1-d,1+d), **kwargs) kwargs.update(transform=ax2.transAxes) ax2.plot((-d,+d), (1-d,1+d), **kwargs) ax2.plot((-d,+d), (-d,+d), **kwargs) # 设置刻度和图例 plt.yticks(range(0,10)) ax.legend() plt.tight_layout() plt.show()
关键说明
- 全局画布创建:将
plt.subplots移到循环外,确保所有绘图都在同一个figure中进行 - 样式映射:通过字典为每个ID分配独特的线条样式和颜色,方便区分不同曲线
- 轴对象传递:绘图函数接收已有的ax和ax2,直接在上面绘制数据,避免重复创建轴
- 统一配置:将轴范围、断轴样式、刻度设置等操作放在循环后,只执行一次,提升效率
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

