resample必须在DatetimeIndex或PeriodIndex上使用,先设时间列为索引并转为时间类型;必须接聚合方法如mean();频率'M'为月末、'MS'为月初;升采样需谨慎,fill_method不等于插值。

resample 必须在 DatetimeIndex 或 PeriodIndex 上调用
直接对普通 DataFrame 调用 resample() 会报 TypeError: Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex。Pandas 不会自动识别你某列是时间——必须先把时间列设为索引,且类型得是时间型。
常见错误写法:df.resample('D')(df 索引是默认整数);正确做法是先确保索引是时间类型:
df = df.set_index('date_column')
df.index = pd.to_datetime(df.index)
如果原始列是字符串,pd.to_datetime() 可能静默失败(比如遇到 '2023-13-01'),建议加 errors='coerce' 并检查 df.index.isna().sum()。
resample 后必须接聚合方法,不能单独存在
resample() 返回的是 Resampler 对象,不是结果数据——它只是个“待执行的重采样计划”。不跟聚合函数(如 mean()、sum()、first())就会报 AttributeError: 'Resampler' object has no attribute 'head'。
立即学习“Python免费学习笔记(深入)”;
常用聚合方式包括:
-
.mean():适合连续值(温度、股价) -
.first()或.last():适合保留起始/结束快照(如每日开盘价、收盘价) -
.count():统计每组非空值数量(注意不是行数,NaN 不计入) -
.agg({'col_a': 'mean', 'col_b': 'max'}):多列不同聚合逻辑
别写 df.resample('M') 就完事,后面至少得跟一个聚合动作。
频率字符串 'M' 和 'MS' 区别很大,选错会导致边界偏移
'M' 是月末(Month End),'MS' 是月初(Month Start)。比如 2023-01-15 到 2023-02-15 的数据,用 'M' 会归到 2023-01-31 和 2023-02-28,而不是自然月 1 月、2 月。
创建 Flask + Vue.js 全栈项目,采用单端口架构、uv 依赖管理、结构化日志和生产级前端设计。当用户要求以下操作时触发:创建新的 Flask+Vue 项目、搭建 Web 应用、构建全栈 Python+Vue 应用、初始化 Flask Web 项目、设置单端口 Flask 前后端项目。触发词:新建项目、创建项目、搭建项目、Flask项目、Vue项目。
更隐蔽的问题:如果你数据截止于 2023-03-10,用 'M' 仍会生成 2023-03-31 这个标签(但对应数据为空),而 'MS' 会生成 2023-04-01 标签(同样为空)——这容易让人误以为漏了数据。
建议优先用语义明确的别名:
-
'D'(日)、'W-MON'(周一为周起点) -
'QS-JAN'(季度初,1 月 1 日起) -
'BH'(营业小时,跳过周末和节假日)
查完整列表可运行 pd.tseries.frequencies.get_freq('M') 或看文档中 offset aliases 表。
升采样(upsampling)需谨慎,fill_method 不等于插值
从日频升到小时频(resample('H').ffill())时,ffill、bfill、asfreq 都只是复制值,不会生成新信息。它们不等价于 interpolate()。
如果真需要插值,得显式链式调用:
df.resample('H').mean().interpolate(method='time')
注意两点:
-
interpolate()前必须有聚合(否则resample('H')没结果) -
method='time'会按真实时间间隔加权,比默认'linear'更合理(尤其数据不等距时)
另外,升采样后数据量暴增,内存可能突然吃紧——先用 df.resample('H').size().head() 估算目标行数再操作。
重采样的真正难点不在语法,而在理解你的业务时间粒度是否允许聚合、边界如何定义、缺失时段要不要补零还是留空。这些没法靠 resample() 自动判断,得人来拍板。

















