Matplotlib衰退区间着色后无法缩放问题的解决方案咨询
解决衰退区间着色与图表缩放兼容问题
问题描述
我有一个0/1类型的RECESSION变量,0代表无衰退、1代表衰退,希望用axvspan或fill类方法为衰退对应的时间区间添加着色效果,不采用双Y轴直方图的实现方式。之前找到的方案会强制图表显示完整X轴范围,无法进行缩放操作,需要修复该方案或寻找替代方案。
当前绘图代码:
fig, ax1=plt.subplots(figsize=(5,5)) ax2=ax1.twinx() ax2.fill_between(df.index, 0,1,where=df['RECESSION'], alpha=0.4,transform=ax2.get_xaxis_transform()) ax1.plot(df['REL']['2022-09-01':'2022-12-01'],'b',linewidth=1) ax2.plot(df['US10']['2022-09-01':'2022-12-01'],'r--',linewidth=1)
对应的数据集:
US10 REL RECESSION Dates 2022-06-05 2.92 0.826972 0.0 2022-06-12 3.04 0.821700 1.0 2022-06-19 3.28 0.831073 1.0 2022-06-26 3.09 0.822982 1.0 2022-07-03 2.98 0.823109 0.0 2022-07-10 3.01 0.812530 0.0 2022-07-17 2.96 0.806165 0.0 2022-07-24 2.91 0.807227 0.0 2022-07-31 2.68 0.806655 0.0 2022-08-07 2.68 0.803642 0.0 2022-08-14 2.87 0.817092 0.0 2022-08-21 2.88 0.814631 0.0 2022-08-28 3.03 0.810260 0.0 2022-09-04 3.26 0.820891 0.0 2022-09-11 3.29 0.828855 0.0 2022-09-18 3.45 0.838984 0.0 2022-09-25 3.70 0.839056 0.0 2022-10-02 3.76 0.829300 0.0 2022-10-09 3.83 0.828128 0.0 2022-10-16 3.97 0.835641 0.0 2022-10-23 4.24 0.841246 0.0 2022-10-30 3.96 0.845483 0.0 2022-11-06 4.14 0.855199 1.0 2022-11-13 3.82 0.853006 1.0 2022-11-20 3.77 0.848167 1.0 2022-11-27 3.71 0.852128 1.0 2022-12-04 3.53 0.846362 1.0 2022-12-11 3.48 0.843546 1.0 2022-12-18 3.44 0.844612 1.0 2022-12-25 3.67 0.859020 1.0 2023-01-01 3.83 0.862859 1.0 2023-01-08 3.71 0.872137 1.0 2023-01-15 3.43 0.863680 1.0 2023-01-22 NaN NaN 1.0 2023-01-29 NaN NaN 1.0
解决方案
问题原因分析
之前的方案中,fill_between使用了完整的df.index,加上transform=ax2.get_xaxis_transform(),会强制Matplotlib显示整个X轴时间范围,导致无法缩放。另外,get_xaxis_transform()将Y轴坐标归一化到0-1,但不对数据做时间范围过滤的话,会直接锁定X轴范围。
修复方案1:过滤时间范围后使用fill_between
先截取和绘图一致的时间范围,在该范围内绘制填充,同时避免强制锁定X轴:
import matplotlib.pyplot as plt # 定义绘图的时间范围 start_date = '2022-09-01' end_date = '2022-12-01' filtered_df = df.loc[start_date:end_date] fig, ax1 = plt.subplots(figsize=(5,5)) ax2 = ax1.twinx() # 在过滤后的时间范围内绘制衰退填充,覆盖整个Y轴高度 ax1.fill_between(filtered_df.index, ax1.get_ylim()[0], ax1.get_ylim()[1], where=filtered_df['RECESSION'] == 1, alpha=0.4, color='gray', zorder=0) # 绘制两条曲线 ax1.plot(filtered_df['REL'], 'b', linewidth=1) ax2.plot(filtered_df['US10'], 'r--', linewidth=1) # 可选:设置Y轴标签 ax1.set_ylabel('REL', color='b') ax2.set_ylabel('US10', color='r') plt.show()
修复方案2:使用axvspan遍历衰退区间
提取所有连续的衰退区间,用axvspan逐个绘制,这种方式完全支持缩放,因为每个区间都是独立的轴元素:
import matplotlib.pyplot as plt import pandas as pd # 提取衰退区间的起始和结束日期 df['RECESSION_SHIFT'] = df['RECESSION'].shift(1) # 找到从0变1的起始点 start_points = df[(df['RECESSION'] == 1) & (df['RECESSION_SHIFT'] == 0)].index # 找到从1变0的结束点 end_points = df[(df['RECESSION'] == 0) & (df['RECESSION_SHIFT'] == 1)].index # 处理最后一个区间持续到末尾的情况 if df.iloc[-1]['RECESSION'] == 1: end_points = end_points.append(pd.Index([df.index[-1]])) # 定义绘图时间范围 start_date = '2022-09-01' end_date = '2022-12-01' fig, ax1 = plt.subplots(figsize=(5,5)) ax2 = ax1.twinx() # 绘制所有符合时间范围的衰退区间 for start, end in zip(start_points, end_points): # 只保留在当前绘图范围内的区间部分 actual_start = max(start, pd.Timestamp(start_date)) actual_end = min(end, pd.Timestamp(end_date)) if actual_start < actual_end: ax1.axvspan(actual_start, actual_end, alpha=0.4, color='gray', zorder=0) # 绘制曲线 ax1.plot(df['REL'].loc[start_date:end_date], 'b', linewidth=1) ax2.plot(df['US10'].loc[start_date:end_date], 'r--', linewidth=1) # 可选:设置Y轴标签 ax1.set_ylabel('REL', color='b') ax2.set_ylabel('US10', color='r') plt.show()
关键说明
- 方案1通过过滤数据到绘图范围,避免X轴被强制拉满,同时用
ax1.get_ylim()让填充覆盖整个Y轴高度,不影响双Y轴显示。 - 方案2通过提取连续衰退区间,用
axvspan绘制,完全兼容缩放操作,因为每个区间都是基于轴坐标的元素,缩放时会自动调整显示。 - 两种方案都无需使用
transform=ax2.get_xaxis_transform(),避免了Y轴归一化带来的问题,同时保证缩放功能正常。
内容的提问来源于stack exchange,提问作者user20856754
相关产品推荐
相关产品推荐

