Pandas中如何将分组最后值按索引分配至下一组?
问题描述
我的需求和某类似问题一致,但有两点不同:1)操作对象是Series;2)分组依据在时间索引中而非列。
现有如下时间索引的Series:
2023-08-01 1515000.08 2023-09-01 2629410.80 2023-10-01 2548748.40 2023-11-01 2494398.04 2023-12-01 3397805.34 2024-01-01 3285501.49 2024-02-01 3173978.74 2024-03-01 3139235.65 2024-04-01 2927895.84 2024-05-01 2750708.29 dtype: float64
需要生成新Series,每个值对应上一季度最后一个月的数值,期望结果如下:
2023-08-01 NaN 2023-09-01 NaN 2023-10-01 2629410.80 <--- 2023-09-01的旧值,即Q3末值 2023-11-01 2629410.80 2023-12-01 2629410.80 2024-01-01 3397805.34 <--- 2023-12-01的旧值 2024-02-01 3397805.34 2024-03-01 3397805.34 2024-04-01 3139235.65 <--- 2024-03-01的旧值 2024-05-01 3139235.65 dtype: float64
我当前的实现代码过于冗长:
period_to_val = ( series.groupby( lambda x: get_quarter(date=x) ) .last() .shift() ) data = series.index.map( lambda x: period_to_val[get_end_of_period(date=x, term_length=term_length, fiscal_year_start=fiscal_year_start)] ) result = pd.Series(data=data, index=series.index)
我想要类似SeriesGroupBy.shift()与transform("last")结合的简洁操作,请问有什么优化方案?
优化方案
方法一:自然季度场景(无需自定义财年)
如果使用自然季度(1-3月Q1、4-6月Q2等),可以直接借助时间索引的year和quarter属性实现:
# 提取每个季度的最后一个值,映射到该季度所有日期 quarter_last = series.groupby([series.index.year, series.index.quarter]).transform('last') # 将每个季度的last值整体下移到下一个季度 result = quarter_last.groupby([series.index.year, series.index.quarter]).shift(3)
方法二:自定义财年季度场景
如果需要自定义财年起始(比如财年从7月开始),用pd.Grouper指定季度频率即可:
# 按自定义财年季度分组,提取季度末值并移位 quarter_end_vals = series.groupby(pd.Grouper(freq='Q-JUL')).last().shift() # 将移位后的季度末值映射回原索引的每个日期 result = series.index.to_series().apply(lambda x: quarter_end_vals.loc[x.floor('Q-JUL')])
方法三:链式简化操作
可以把分组、移位、映射合并为更紧凑的链式代码:
result = pd.Series( series.index.map(lambda x: series.groupby(pd.Grouper(freq='Q-JUL')).last().shift().loc[x.floor('Q-JUL')]), index=series.index )
核心逻辑
- 按季度分组提取每个季度的最后一个值;
- 将季度末值整体下移一个季度;
- 把偏移后的值映射回原Series的所有日期,同一季度内的日期共享对应的上季度末值。
内容的提问来源于stack exchange,提问作者bhub
相关产品推荐
相关产品推荐

