Pandas如何实现非常规groupby:年末日期同时归属相邻两个年度分组
实现方案
针对大体量DataFrame场景,采用pandas原生矢量化操作实现,全程无循环、无逐行遍历,性能开销极低。核心逻辑:每年仅需额外复制1条12月最后一个可见日期的记录,将其分组标记设为下一年,即可满足规则要求,不需要构建样例中占内存的稀疏宽表(样例中g1~g6仅为效果展示,实际分组计算无需生成这类列)。
实现逻辑
- 先将
date列转换为pandas datetime类型,为后续日期计算提速 - 为每条记录添加基础分组标记:即日期对应的自然年
- 按自然年分组,筛选出每年12月的最大日期(即每年12月最后一个可见的观测日)
- 将筛选出的年末日期记录复制一份,分组标记改为对应年份+1
- 把原数据集和复制出的追加记录合并,直接按分组标记做groupby即可,完全符合规则要求
可直接运行的代码
import pandas as pd # 加载样例数据 data = {'date': ['2017-03-31', '2017-04-03', '2017-12-27', '2017-12-28', '2017-12-29', '2018-01-02', '2018-12-31', '2019-01-02', '2019-01-03', '2019-12-31', '2020-12-30', '2020-12-31', '2021-01-20', '2021-12-30', '2021-12-31', '2022-05-30', '2022-05-31'] } df = pd.DataFrame(data) # 转换日期格式 df['date'] = pd.to_datetime(df['date']) # 生成基础分组年份:日期本身所属自然年 df['group_year'] = df['date'].dt.year # 提取每年12月的最后一个可见日期 dec_last_dates = ( df[df['date'].dt.month == 12] # 筛选所有12月的记录 .groupby(df['date'].dt.year)['date'] .max() # 取每年12月的最大日期,即最后一个观测日 .reset_index(drop=True) .to_frame() ) # 给年末日期副本设置分组年份为下一年 dec_last_dates['group_year'] = dec_last_dates['date'].dt.year + 1 # 合并原数据与追加的年末副本,即可直接用于分组计算 df_for_calc = pd.concat([df, dec_last_dates], ignore_index=True)
效果验证
如果需要复现样例中的宽表展示效果,对计算用表做一次透视即可,结果和给出的期望分组完全一致:
# 生成样例式宽表 show_result = ( df_for_calc.assign(val=1) .pivot(index='date', columns='group_year', values='val') .fillna('') .sort_index() ) # 列名对齐样例的g1~g6格式 show_result.columns = [f'g{i+1}' for i in range(len(show_result.columns))] print(show_result)
运行后可以看到:每年12月的最后一个可见日期会同时归属当年和下一年分组,其余日期仅归属自身自然年分组,和需求完全匹配。
该方案对千万级以上大体量数据友好度极高:无论时间跨度多长,每年仅新增1条记录,全程矢量化运算没有逐行判断的性能损耗,相比循环、
apply类方法效率高出1~2个数量级,内存占用几乎无增长。
内容的提问来源于stack exchange,提问作者Fcoatis
相关产品推荐
相关产品推荐

