Pandas resample异常:Datetime Index分组求和结果不符合预期
Pandas resample半月分组分箱错误的解决方法
原始DataFrame:
Column1 2022-08-03 08:48:34 9217.02 2022-08-17 17:14:39 6229.27 2022-08-31 17:17:00 6229.27 2022-09-14 18:12:14 5939.54 2022-09-30 17:51:48 6229.27 2022-10-14 15:26:14 5939.54 2022-10-31 16:29:14 5939.54 2022-11-15 18:10:27 5939.54 2022-11-30 18:10:23 5939.54 2022-12-19 10:53:21 5939.54 2022-12-20 16:26:08 2440.98 2022-12-30 18:30:25 6302.54 2023-01-13 19:24:22 6262.74 2023-01-31 16:51:44 6262.74
期望按每月1日至15日、16日至月末分组求和,输出如下:
Column1 2022-08-15 9217.02 2022-08-31 12458.54 2022-09-15 5939.54 2022-09-30 6229.27 2022-10-15 5939.54 2022-10-31 5939.54 2022-11-15 5939.54 2022-11-30 5939.54 2022-12-15 0.0 2022-12-31 14683.06 2023-01-15 6262.74 2023-01-31 6262.74
问题原因
直接调用df.resample('SM').sum()出现分箱错误,核心原因是pandas默认的SM(半月频率)分组逻辑:
- 默认以每月15日和月末为锚点,标签取区间左边界
- 分组区间为「上月末至本月15日」「本月15日至本月末」,和需求的「1-15日」「16-月末」不匹配
使用label='right'虽能修正标签,但会生成超出数据范围的分组(如2023-02-15),不符合预期。
解决方案
方法一:自定义分组键(直观易控)
通过判断日期的日数,手动生成对应分组标签,再按标签求和:
import pandas as pd # 遍历索引日期,生成分组键 group_keys = [] for date in df.index.date: if date.day <= 15: # 1-15日的分组标签为当月15日 group_key = date.replace(day=15) else: # 16-月末的分组标签为当月最后一天 # 计算当月最后一天的通用方法 next_month = date.replace(day=28) + pd.Timedelta(days=4) group_key = next_month - pd.Timedelta(days=next_month.day) group_keys.append(pd.to_datetime(group_key)) # 按分组键求和 result = df.groupby(group_keys).sum() # 生成所有需要的日期区间,补全缺失分组并填充0 min_month = df.index.min().to_period('M') max_month = df.index.max().to_period('M') all_dates = [] for period in pd.period_range(min_month, max_month): all_dates.append(period.start_time.replace(day=15)) all_dates.append(period.end_time) result = result.reindex(all_dates, fill_value=0)
方法二:调整resample参数
通过修改resample的label、closed、loffset参数,匹配需求区间:
# 调整resample参数,让区间和标签匹配需求 result = df.resample('SM', label='right', closed='right', loffset='1D').sum() # 过滤超出数据范围的分组 max_end_date = df.index.max().to_period('M').end_time result = result.loc[:max_end_date] # 补全缺失日期并填充0 min_start_date = df.index.min().replace(day=15) all_dates = pd.date_range(start=min_start_date, end=max_end_date, freq='SM') result = result.reindex(all_dates, fill_value=0)
参数说明:
label='right':标签取区间右边界(即15日或月末)closed='right':区间为左开右闭,确保15日当天数据归入1-15日分组loffset='1D':修正默认锚点,避免将上月末至本月15日作为一个区间
内容的提问来源于stack exchange,提问作者iXrst
相关产品推荐
相关产品推荐

