
使用 pandas 的 resample + ffill 将日频数据转为小时频时,末尾日期常被截断——因重采样默认右边界不闭合,导致最后一天仅生成一个时间点(如 2024-01-02 00:00)。本文提供可靠解决方案:扩展索引+后过滤,确保每日均生成完整24小时记录。
使用 pandas 的 resample + ffill 将日频数据转为小时频时,末尾日期常被截断——因重采样默认右边界不闭合,导致最后一天仅生成一个时间点(如 2024-01-02 00:00)。本文提供可靠解决方案:扩展索引+后过滤,确保每日均生成完整24小时记录。
在使用 DataFrame.resample("h").ffill() 进行日→小时上采样时,一个常见却易被忽略的问题是:末尾日期无法生成完整的24小时记录。其根本原因在于 pandas 的 resample 默认采用左闭右开区间(closed='left', label='left'),当原始索引最大值为 2024-01-02 00:00:00(即纯日期转为午夜时间戳)时,重采样生成的时间桶覆盖范围为 [2024-01-01 00:00, 2024-01-02 00:00),因此 2024-01-02 仅贡献起始时刻,后续23小时无对应源值可前向填充。
以下是最简复现与推荐解法:
import pandas as pd
# 原始日频数据(2天)
T1 = pd.Timestamp("2024-01-01")
T2 = pd.Timestamp("2024-01-02")
df = pd.DataFrame({"T": [T1, T2], "V": [1, 2]})
# ❌ 错误做法:直接 resample → 仅得25行(24h for T1 + 1h for T2)
result_bad = df.set_index("T").resample("h").ffill()
print(f"错误结果长度: {len(result_bad)}") # 输出: 25
# ✅ 正确做法:扩展索引至下一自然日,再前向填充并清理
T3 = T2 + pd.Timedelta("1D") # 添加占位日期:2024-01-03
df_extended = pd.DataFrame({
"T": [T1, T2, T3],
"V": [1, 2, pd.NA] # 末尾值设为 NA,避免污染
})
result_good = (df_extended
.set_index("T")
.resample("h", closed="left") # 显式声明边界(可选,增强可读性)
.ffill()
.dropna(subset=["V"])) # 精准剔除扩展引入的无效行
print(f"正确结果长度: {len(result_good)}") # 输出: 48
print(result_good.head(3))
# V
# T
# 2024-01-01 00:00:00 1.0
# 2024-01-01 01:00:00 1.0
# 2024-01-01 02:00:00 1.0
关键要点说明:
- ✅ 扩展索引是核心技巧:添加一个超出原数据范围的“哨兵时间点”(如 T2 + 1D),迫使 resample 生成覆盖 T2 全天的24个桶;
- ✅ dropna(subset=["V"]) 比 dropna() 更安全:避免因其他列存在空值而误删有效行;
- ⚠️ 无需修改 closed/label 参数:默认行为已足够,显式设置仅用于代码自解释;
- ? 通用性验证:该方法对任意长度的日频序列均适用——只需在末尾追加 max_date + 1D 即可。
最终,你将获得严格按小时对齐、每日24行、末尾日期完整填充的理想结果,彻底规避 pandas 重采样在边界处理上的“隐形截断”陷阱。











