pandas.resample/groupby实现每月10/20/月末为节点的自定义时间间隔聚合
实现方案
核心思路
通过自定义规则把每条数据映射到所属区间的结束日期,再按结束日期分组聚合即可,完美适配每月10/20/月末三个节点的区间规则,不会出现月初日期漏算的问题。
完整代码实现
import pandas as pd import numpy as np from pandas.tseries.offsets import MonthEnd # 定义区间结束日期映射函数 def get_period_end(date): day = date.day year, month = date.year, date.month if day <= 10: return pd.Timestamp(year=year, month=month, day=10) elif 11 <= day <= 20: return pd.Timestamp(year=year, month=month, day=20) else: # 大于20号则取当月最后一天 return pd.Timestamp(year=year, month=month, day=1) + MonthEnd(1) # ========== 测试用例1:初始样例数据 ========== d = {'price': [10, 12, 8, 12, 14, 18, 10, 20], 'volume': [50, 60, 40, 100, 50, 100, 40, 50]} df = pd.DataFrame(d) df['a_date'] = pd.date_range('01/01/2018', periods=8, freq='W') # 映射区间结束日期、分组求和、修改索引名匹配预期输出 df['period_end'] = df['a_date'].apply(get_period_end) result = df.groupby('period_end')[['price', 'volume']].sum().rename_axis('a_date') print("样例1输出:") print(result) # ========== 测试用例2:包含月初第一天的20天数据 ========== d2 = {'price': np.arange(20)+1, 'volume': np.arange(20)+5} df2 = pd.DataFrame(d2) df2['a_date'] = pd.date_range('01/01/2018', periods=20, freq='D') df2['period_end'] = df2['a_date'].apply(get_period_end) result2 = df2.groupby('period_end')[['price', 'volume']].sum().rename_axis('a_date') print("\n样例2输出:") print(result2)
输出验证
样例1输出完全匹配预期:
price volume a_date 2018-01-10 10 50 2018-01-20 12 60 2018-01-31 20 140 2018-02-10 14 50 2018-02-20 28 140 2018-02-28 20 50
样例2输出符合正确计算结果,无月初日期漏算:
price volume a_date 2018-01-10 55 95 2018-01-20 155 195
可选扩展:补全空区间
如果需要展示所有区间(包括无数据的区间),可以先生成全量的区间结束日期,再对结果重索引:
# 生成时间范围内所有不重复的区间结束日期 start = df['a_date'].min().replace(day=1) end = df['a_date'].max() + MonthEnd(1) all_dates = pd.date_range(start=start, end=end, freq='D') all_periods = sorted(list(set([get_period_end(d) for d in all_dates]))) # 重索引补全空值,无数据的区间填充0 result = result.reindex(all_periods, fill_value=0).rename_axis('a_date')
内容的提问来源于stack exchange,提问作者duff18
相关产品推荐
相关产品推荐

