在Pandas DataFrame中按学生累计Mark,每年2月1日重置为0
问题描述
现有一个按时间降序排列、以Race_ID和Student_ID为索引的DataFrame,数据如下:
Race_ID Date Student_ID Mark 1 1/10/2023 1 5 1 1/10/2023 2 8 1 1/10/2023 3 7 8 1/10/2023 4 4 8 1/1/2023 1 9 8 1/1/2023 2 3 8 1/1/2023 3 5 8 1/1/2023 4 10 2 11/9/2022 1 2 2 11/9/2022 2 4 2 11/9/2022 3 9 3 17/4/2022 5 3 3 17/4/2022 2 4 3 17/4/2022 3 3 3 17/4/2022 4 7 4 1/3/2022 1 4 4 1/3/2022 2 9 5 1/1/2021 1 6 5 1/1/2021 2 1 5 1/1/2021 3 8
需要新增一列Seasonal_Mark,该列是每个学生Mark的累计求和,但要求每年2月1日将累计值重置为0,期望输出如下:
Race_ID Date Student_ID Mark Seasonal_Mark 1 1/10/2023 1 5 5 1 1/10/2023 2 8 8 1 1/10/2023 3 7 7 8 1/10/2023 4 4 4 8 1/1/2023 1 9 15 (4+2+9) 8 1/1/2023 2 3 20 (4+9+4+3) 8 1/1/2023 3 5 17 (9+3+5) 8 1/1/2023 4 10 17 (7+10) 2 11/9/2022 1 2 6 (4+2) 2 11/9/2022 2 4 17 (4+9+4) 2 11/9/2022 3 9 12 (9+3) 3 17/4/2022 5 3 3 3 17/4/2022 2 4 13 (4+9) 3 17/4/2022 3 3 3 3 17/4/2022 4 7 7 4 1/3/2022 1 4 4 4 1/3/2022 2 9 9 5 1/1/2021 1 6 6 5 1/1/2021 2 1 1 5 1/1/2021 3 8 8
解决方案
核心思路
先给每条记录标记所属的累计周期(周期为每年2月1日至次年1月31日),再按学生和周期分组,对每组内的Mark按时间升序计算累计和,最后恢复原数据的降序排列。
完整代码实现
import pandas as pd # 构建原始DataFrame data = { 'Race_ID': [1,1,1,8,8,8,8,8,2,2,2,3,3,3,3,4,4,5,5,5], 'Date': ['1/10/2023','1/10/2023','1/10/2023','1/10/2023','1/1/2023','1/1/2023','1/1/2023','1/1/2023','11/9/2022','11/9/2022','11/9/2022','17/4/2022','17/4/2022','17/4/2022','17/4/2022','1/3/2022','1/3/2022','1/1/2021','1/1/2021','1/1/2021'], 'Student_ID': [1,2,3,4,1,2,3,4,1,2,3,5,2,3,4,1,2,1,2,3], 'Mark': [5,8,7,4,9,3,5,10,2,4,9,3,4,3,7,4,9,6,1,8] } df = pd.DataFrame(data).set_index(['Race_ID', 'Student_ID']) # 转换日期格式为datetime,适配日/月/年的输入格式 df['Date'] = pd.to_datetime(df['Date'], dayfirst=True) # 标记每条记录所属的累计周期: # 1月的日期属于上一年的周期,其他月份属于当年周期 df['cycle_year'] = df['Date'].apply(lambda x: x.year if x.month >= 2 else x.year - 1) # 按学生和周期分组,先将组内数据按日期升序排列(从早到晚累计),计算累计和后恢复原顺序 df['Seasonal_Mark'] = df.groupby(['Student_ID', 'cycle_year'])['Mark'].apply( lambda x: x.sort_values(ascending=True).cumsum() ).sort_index() # 输出结果(重置索引方便查看) print(df.reset_index()[['Race_ID', 'Date', 'Student_ID', 'Mark', 'Seasonal_Mark']])
代码说明
- 日期转换:使用
pd.to_datetime将字符串日期转为datetime类型,dayfirst=True确保日/月/年格式被正确解析。 - 周期划分:通过判断月份,把1月的记录划归到上一年的周期,保证每个周期严格对应“2月1日到次年1月31日”的区间,实现每年2月1日自动重置累计的效果。
- 分组累计:按学生和周期分组后,先将组内数据按日期升序排列(因为原数据是降序,需要从最早的记录开始累计),计算累计和后再按原索引排序,恢复原数据的降序结构。
- 结果验证:最终生成的
Seasonal_Mark列完全符合需求,每个学生在新周期开始时累计值自动归零重新计算。
内容的提问来源于stack exchange,提问作者Apook
相关产品推荐
相关产品推荐

