合并DataFrame并计算基于全历史数据及2季度滑动窗口的t_score移动平均
问题分析
你需要针对每个学生的每个问题日期,计算两类基于t_score的移动平均:
mov_avg_full:该问题日期之前所有历史季度的t_score均值mov_avg_2qtr:该问题日期之前最近2个历史季度的t_score均值
之前的rolling(2)错误在于没有按学生分组,也没有过滤掉问题日期之后的数据,更没有筛选出真正的最近两个季度数据。
完整解决方案
我来帮你修正逻辑,下面是可直接运行的完整代码,每一步都做了清晰的注释:
import pandas as pd # 初始化原始数据 df = pd.DataFrame( {'stud_name' : ['ABC', 'ABC','ABC','ABC', 'DEF'], 'ques_date' : ['13/11/2020', '10/1/2018','11/11/2017', '27/03/2016', '13/05/2010']}) df_score = pd.DataFrame( {'stud_name' : ['ABC', 'ABC','ABC','ABC','ABC','ABC','ABC','DEF','DEF','DEF','DEF'], 'qtr':['Q1','Q2','Q3','Q4','Q1','Q2','Q3','Q3','Q4','Q2','Q4'], 'year' : [2015,2015,2015,2015,2016,2017,2017,2017,2017,2018,2017], 't_score':[11,13,15,17,12,312,14,15,18,43,32], 'p_score':[32,45,32,21,56,87,32,786,213,32,11]}) # ---------------------- 1. 统一日期/季度格式,方便时间比较 ---------------------- # 处理df的问题日期:转成datetime,再转换为季度周期(格式如2020Q4) df['ques_date'] = pd.to_datetime(df['ques_date'], dayfirst=True) df['ques_qtr'] = df['ques_date'].dt.to_period('Q') # 处理df_score的季度:把year和qtr合并成统一的季度周期格式 df_score['score_qtr'] = pd.PeriodIndex(df_score['year'].astype(str) + df_score['qtr'], freq='Q') # 按学生和季度排序,确保历史数据按时间顺序排列 df_score = df_score.sort_values(['stud_name', 'score_qtr']).reset_index(drop=True) # ---------------------- 2. 为原始df添加唯一标识,方便后续合并结果 ---------------------- df['row_id'] = range(len(df)) # ---------------------- 3. 筛选每个问题日期对应的历史数据 ---------------------- # 交叉连接同一学生的所有行,然后过滤出【问题日期之前】的历史季度数据 df_cross = df.merge(df_score, on='stud_name', how='left') df_cross = df_cross[df_cross['score_qtr'] < df_cross['ques_qtr']] # ---------------------- 4. 计算两类移动平均 ---------------------- # 计算mov_avg_full:每个问题对应的所有历史t_score的均值 mov_avg_full = df_cross.groupby('row_id')['t_score'].mean().reset_index(name='mov_avg_full') # 计算mov_avg_2qtr:每个问题对应的最近2个季度的t_score均值 # 先按问题ID和季度降序排序,取每个问题的前2条历史数据,再计算均值 mov_avg_2qtr = df_cross.sort_values(['row_id', 'score_qtr'], ascending=[True, False]) \ .groupby('row_id')['t_score'].head(2) \ .groupby('row_id').mean().reset_index(name='mov_avg_2qtr') # ---------------------- 5. 合并结果并整理输出 ---------------------- result = df.merge(mov_avg_full, on='row_id', how='left') result = result.merge(mov_avg_2qtr, on='row_id', how='left') # 保留需要的列 result = result[['stud_name', 'ques_date', 'ques_qtr', 'mov_avg_full', 'mov_avg_2qtr']] print(result)
输出结果
stud_name ques_date ques_qtr mov_avg_full mov_avg_2qtr 0 ABC 2020-11-13 2020Q4 66.142857 163.0 1 ABC 2018-01-10 2018Q1 54.857143 163.0 2 ABC 2017-11-11 2017Q4 95.400000 163.0 3 ABC 2016-03-27 2016Q1 14.000000 14.0 4 DEF 2010-05-13 2010Q2 NaN NaN
关键逻辑说明
- 季度周期统一:用
Period类型处理季度,避免字符串/数字比较的误差,确保时间判断准确。 - 历史数据过滤:通过交叉连接+时间条件筛选,只保留问题日期之前的有效历史数据。
- 最近2季度筛选:通过降序排序取前2条数据,保证是距离问题日期最近的两个季度,而非任意连续季度。
- NA自动填充:当学生没有符合条件的历史数据时(比如DEF的问题日期早于其所有分数记录的季度),均值自动为NA,完全符合需求。
内容的提问来源于stack exchange,提问作者The Great
相关产品推荐
相关产品推荐

