必须先将时间列转为datetime64[ns]并设为datetimeindex,才能用resample('m')按月聚合;'m'按月末对齐,'ms'按月初对齐;时区和夏令时需统一转utc处理。

用 resample() 按月聚合,但必须确保索引是 DatetimeIndex
直接调用 resample('M') 报错 TypeError: Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex?说明你的 DataFrame 索引不是时间类型。Pandas 不会自动把列当时间索引处理,哪怕列名叫 'date' 或值看起来像日期。
实操建议:
- 先确认时间列是否已转为
datetime64[ns]:用df['date'].dtypes查看,如果不是,先执行df['date'] = pd.to_datetime(df['date']) - 再设为索引:
df = df.set_index('date')(注意:这步不可跳过) - 之后才能用
df.resample('M').sum()、.mean()等聚合
'M' 和 'MS' 的区别:月末 vs 月初对齐
按月重采样时,'M' 表示“月末频率”(Month End),生成的分组标签是每月最后一天(如 2023-01-31);'MS' 是“月初频率”(Month Start),标签是每月第一天(如 2023-01-01)。两者分组逻辑一致,只是输出的时间戳不同。
常见错误现象:用 'M' 聚合后画图,x 轴显示 1 月 31 日,但你想标成“Jan 2023”,这时别改数据,改绘图时的格式化(例如用 matplotlib.dates.MonthLocator() + strftime('%b %Y'))更稳妥。
使用场景提示:
- 财务统计常用
'MS',因为账期通常从月初开始 - 传感器日志若只在工作日有数据,
'M'可能导致某月无记录(因月末是周末),此时考虑用'MS'或补全缺失月份(resample(...).asfreq())
聚合函数选 first()/last() 还是 agg()?关键看业务含义
时间序列中,“当月第一个值”和“当月最后一个值”常有特定意义(如开盘价/收盘价、月初配置快照/月末状态)。直接用 .first() 或 .last() 比写 .agg({'col': 'first'}) 更简洁,且天然保留原始 dtype。
但要注意:
-
.first()遇到空组默认返回NaN,不会报错;若需跳过空组,加参数dropna=False不起作用,得用.apply(lambda x: x.iloc[0] if not x.empty else pd.NA) - 混合聚合(比如对
price取均值、对volume取和)必须用.agg({'price': 'mean', 'volume': 'sum'}),不能链式调用多个聚合方法 -
.ohlc()是快捷方式,返回Open/High/Low/Close四列,等价于.agg({'col': ['first', 'max', 'min', 'last']})
遇到时区或夏令时数据,resample() 会静默出错
如果时间列带时区(如 2023-03-12 02:15:00-05:00),而当天是夏令时切换日(美国东部时间 2:00 跳到 3:00),resample('M') 可能漏掉一小时的数据,或把两个不同时段压进同个 bin——Pandas 默认按本地时区解释,不校正 DST 边界。
安全做法:
- 统一转为 UTC:
df['date'] = df['date'].dt.tz_convert('UTC'),再set_index和resample - 避免用本地时区字符串(如
'US/Eastern')直接 resample,除非你明确处理了ambiguous和nonexistent参数 - 检查结果行数:对已知有 12 个月数据的年份,
resample('M').size().count()应为 12;少于这个数,大概率是时区或缺失时间戳导致分组断裂
时区和 DST 处理不是可选项,而是重采样结果可信的前提。漏掉这点,后面所有统计都可能偏移一天甚至整个周期。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











