双轴时间序列图合并异常:箱线图与折线图X轴问题排查
问题解决:合并箱线图与折线图的X轴异常问题
问题现象
尝试合并两个时间序列图:箱线图(展示Midday Stem Water Potential)和折线图(展示Cumulative Applied Irrigation),两者共用X轴,左、右分别对应不同Y轴。单独绘制时均正常,但合并后出现箱线图被挤压、X轴起始日期错误的问题。
核心原因
代码中错误地将日期列转换为字符串格式(dt.strftime('%m-%d')),导致:
- Seaborn的
boxplot将X轴识别为分类变量而非连续时间轴,箱线图会被压缩到分类刻度的固定位置 - 折线图的X轴字符串无法与连续时间轴匹配,
twinx后的轴设置(如日期格式化)完全失效,最终导致X轴范围混乱
解决步骤
- 保留日期的datetime类型:不要将日期列转为字符串,让Matplotlib识别为连续时间轴
- 统一X轴时间范围:基于两个数据集的最小/最大日期,设置统一的X轴范围
- 正确配置日期刻度格式:对主副轴的X轴统一设置日期显示格式
- 匹配箱线图时间位置:让箱线图直接对应到时间轴的正确节点
修正后的完整代码
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import matplotlib.dates as mdates # 加载数据 file_path_irrigation = '/Users/ajgal/Documents/applied_irrigation.xlsx' file_path_swp = '/Users/ajgal/Documents/combined_SWP_2024.xlsx' # 读取灌溉数据:保留datetime类型 irr = pd.read_excel(file_path_irrigation, sheet_name='Sheet1') irr['Event'] = pd.to_datetime(irr['Event'], format='%Y-%m-%d') irr.sort_values('Event', inplace=True) # 读取SWP数据:保留datetime类型 SWP = pd.read_excel(file_path_swp) SWP['reading_bar'] = SWP['reading_bar'] / 10 SWP['Sampling'] = pd.to_datetime(SWP['Sampling'], format='%Y-%m-%d') SWP.sort_values('Sampling', inplace=True) # 确定统一的X轴时间范围 min_date = min(irr['Event'].min(), SWP['Sampling'].min()) max_date = max(irr['Event'].max(), SWP['Sampling'].max()) # 绘图 fig, ax1 = plt.subplots(figsize=(22, 9)) color_DRI = 'orange' color_drip = 'darkblue' # 绘制箱线图(主Y轴:茎水势) ax1.set_ylim(-2, 0) ax1.set_ylabel("Midday Stem Water Potential (MPa)", color='black', fontsize=20) # 使用datetime作为x轴,hue区分处理组 sns.boxplot(x=SWP['Sampling'], y=SWP['reading_bar'], hue=SWP['block'], palette={'DRI': color_DRI, 'Micro Drip': color_drip}, width=0.7, ax=ax1) ax1.tick_params(axis='y', labelsize=16) ax1.tick_params(axis='x', labelsize=16, rotation=45) ax1.grid(False) # 设置主X轴的日期格式 ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d')) ax1.set_xlim(min_date, max_date) # 处理主图例 handles1, labels1 = ax1.get_legend_handles_labels() ax1.legend(handles1, labels1, loc=2, prop={'size': 16}) # 创建副Y轴(累计灌溉量) ax2 = ax1.twinx() ax2.set_ylabel('Cumulative Applied Irrigation (mm)', color='black', fontsize=20) # 绘制折线图,使用datetime类型的Event列 ax2.plot(irr['Event'], irr['B4N_mm_cum'], color=color_DRI, label='DRI', linewidth=2) ax2.plot(irr['Event'], irr['B4S_mm_cum'], color=color_drip, label='Micro Drip', linewidth=2) ax2.tick_params(axis='y', labelsize=16) ax2.grid(False) # 副轴共享主X轴的范围和格式,无需重复设置 # 处理副图例 handles2, labels2 = ax2.get_legend_handles_labels() ax2.legend(handles2, labels2, loc=1, prop={'size': 16}) plt.tight_layout() plt.show()
关键调整说明
- 移除所有
dt.strftime('%m-%d')转换,保留日期列的datetime类型,让Matplotlib以连续时间轴处理X轴 - 计算两个数据集的最小/最大日期,统一设置X轴范围,确保起始日期正确
- 主副轴共享X轴范围,无需重复设置刻度格式,避免冲突
- 箱线图直接使用datetime作为x参数,自动对应到时间轴的正确位置,解决挤压问题
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

