You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

双轴时间序列图合并异常:箱线图与折线图X轴问题排查

问题解决:合并箱线图与折线图的X轴异常问题

问题现象

尝试合并两个时间序列图:箱线图(展示Midday Stem Water Potential)和折线图(展示Cumulative Applied Irrigation),两者共用X轴,左、右分别对应不同Y轴。单独绘制时均正常,但合并后出现箱线图被挤压、X轴起始日期错误的问题。

核心原因

代码中错误地将日期列转换为字符串格式(dt.strftime('%m-%d')),导致:

  • Seaborn的boxplot将X轴识别为分类变量而非连续时间轴,箱线图会被压缩到分类刻度的固定位置
  • 折线图的X轴字符串无法与连续时间轴匹配,twinx后的轴设置(如日期格式化)完全失效,最终导致X轴范围混乱

解决步骤

  1. 保留日期的datetime类型:不要将日期列转为字符串,让Matplotlib识别为连续时间轴
  2. 统一X轴时间范围:基于两个数据集的最小/最大日期,设置统一的X轴范围
  3. 正确配置日期刻度格式:对主副轴的X轴统一设置日期显示格式
  4. 匹配箱线图时间位置:让箱线图直接对应到时间轴的正确节点

修正后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 19:15:55