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

Matplotlib datetime X轴全量数据下月首刻度格式化异常问题

问题描述

对2021年逐小时时间序列数据做可视化时,X轴为datetime64[ns]类型的时间值,目标配置为仅在每月1日(1-12月)显示主刻度。实际运行时出现两类表现:

  • 加载全量数据点时,X轴刻度标签显示错误年份,刻度总数远超出预期
  • 删除大比例数据后,刻度标签可正常显示,但会丢失原始数据
    需要在保留全部数据的前提下,实现X轴仅在每月首日显示刻度与对应标签的效果。

复现代码

import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

times=pd.date_range(start='2021-01-01', end='2021-12-31', freq='1h')
adf = pd.DataFrame({
    'time': times,
    'A': np.random.rand(times.shape[0]),
    'B': np.random.rand(times.shape[0]) * 100
})

# 取消注释删除大比例数据后,刻度可正常格式化
# for col in adf.columns:
#     adf.loc[adf.sample(frac=0.7).index, col] = np.nan

adf.set_index('time', inplace=True)

fig, ax = plt.subplots(dpi=300)

ax2 = ax.twinx()

a = adf.plot(y='A', ax=ax, color='blue', alpha=0.3, legend=False)
b = adf.plot(y='B', ax=ax2, color='red', alpha=0.3, legend=False)

ax.legend([
    a.get_lines()[0],
    b.get_lines()[0]
], ['A', 'B'])

ax.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d-%y'))
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=1))
ax.tick_params(axis='x', rotation=30)

异常表现

全量数据场景刻度异常

全量数据加载时,刻度及标签数量异常偏多,执行ax.get_xticklabels()返回结果如下,标签显示为1994年的错误日期:

[Text(447088.0, 0, '02-01-94'),
 Text(447116.0, 0, '03-01-94'),
 Text(447147.0, 0, '04-01-94'),
 Text(447177.0, 0, '05-01-94'),
 Text(447208.0, 0, '06-01-94'),
 Text(447238.0, 0, '07-01-94'),
 Text(447269.0, 0, '08-01-94'),
 Text(447300.0, 0, '09-01-94'),
 Text(447330.0, 0, '10-01-94'),
 Text(447361.0, 0, '11-01-94'), ...]

全量数据时刻度显示错误

删减量数据场景刻度正常

删除70%左右数据后,刻度可正常显示对应数量,但会丢失原始数据,效果如下:
删除大量数据后刻度格式化正常,但会丢失数据

问题原因

出现该问题的核心是pandas默认绘图逻辑的冲突:

  • 当绘制的时间序列数据无缺失值、点密度较高时,pandas会启用自带的高密度时间刻度优化逻辑,自动生成小时/日级的刻度规则,后续手动设置的matplotlib日期刻度配置不会覆盖这套规则
  • 你看到的1994年错误标签,本质是pandas生成的刻度值为距时间起点的小时偏移量,被DateFormatter误解析为1900年代的日期
  • 当数据中存在大量NaN值时,pandas会自动切换回matplotlib原生的日期刻度模式,后续手动设置的locator和formatter就会生效,也就是删数据后刻度正常的原因
解决方案

在调用pandas的plot()方法时传入x_compat=True参数,禁用pandas自带的时间刻度自动优化逻辑,让后续手动配置的matplotlib日期刻度规则正常生效。如果需要严格限定仅每月1日生成刻度,可以给MonthLocator额外传入bymonthday=1参数。

修正后的完整可运行代码如下:

import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

times=pd.date_range(start='2021-01-01', end='2021-12-31', freq='1h')
adf = pd.DataFrame({
    'time': times,
    'A': np.random.rand(times.shape[0]),
    'B': np.random.rand(times.shape[0]) * 100
})

adf.set_index('time', inplace=True)

fig, ax = plt.subplots(dpi=300)
ax2 = ax.twinx()

# 关键修改:传入x_compat=True禁用pandas自动刻度逻辑
a = adf.plot(y='A', ax=ax, color='blue', alpha=0.3, legend=False, x_compat=True)
b = adf.plot(y='B', ax=ax2, color='red', alpha=0.3, legend=False, x_compat=True)

ax.legend([
    a.get_lines()[0],
    b.get_lines()[0]
], ['A', 'B'])

# 配置仅每月1日显示主刻度
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=1, bymonthday=1))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d-%y'))
ax.tick_params(axis='x', rotation=30)

# 自动调整布局避免标签截断
plt.tight_layout()
plt.show()

运行后即可在保留全量逐小时数据的前提下,实现X轴仅在每月1日显示正确的2021年日期刻度标签。


内容的提问来源于stack exchange,提问作者K. Shores

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:03:25