Pandas分组后获取满足30天间隔的前3条历史值
解决按分组获取间隔至少30天的历史值问题
我明白你遇到的问题了——要按Param1和Param2分组,为每行创建prev_1、prev_2、prev_3三个列,分别对应与当前行日期间隔至少30天的最近3条历史value值,而且直接用shift不行,因为它只偏移固定行数,没法判断时间间隔。
下面是一个高效的实现方案,不用全局循环,仅在分组内做小范围循环,既保证可读性又能准确满足需求:
步骤1:准备数据并转换日期格式
首先得把date列转成datetime类型,这样才能计算时间差:
import pandas as pd # 构造你提供的示例数据 data = { 'Param1': ['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b'], 'Param2': ['b', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c'], 'date': ['30/10/2007', '31/10/2007', '01/11/2007', '01/12/2007', '02/12/2007', '01/03/2008', '05/10/2008', '06/10/2008', '07/10/2008', '08/11/2008', '09/11/2008', '08/12/2008'], 'value': [5, 8, 9, 3, 2, 11, 7, 13, 19, 22, 35, 5] } df = pd.DataFrame(data) # 将date列转换为datetime格式,指定输入格式为dd/mm/yyyy df['date'] = pd.to_datetime(df['date'], format='%d/%m/%Y')
步骤2:定义分组处理函数
这个函数会处理每个分组内的行,筛选出符合时间条件的历史记录,再提取最近3条值:
def get_prev_historical_values(group): # 先按日期排序,确保历史记录是按时间先后排列的 group = group.sort_values('date').reset_index(drop=True) # 初始化三个结果列 group['prev_1'] = None group['prev_2'] = None group['prev_3'] = None for idx in range(len(group)): current_date = group.loc[idx, 'date'] # 筛选出当前日期之前、且间隔至少30天的记录 valid_historical_mask = (group['date'] <= current_date - pd.Timedelta(days=30)) valid_values = group.loc[valid_historical_mask, 'value'].tolist() # 取最近的3条(因为已经按日期排序,列表最后3个就是最近的) # 反转后让第一个元素是最近的,对应prev_1,以此类推 recent_3_values = valid_values[-3:][::-1] # 填充到对应的列中 for val_idx, val in enumerate(recent_3_values): if val_idx == 0: group.loc[idx, 'prev_1'] = val elif val_idx == 1: group.loc[idx, 'prev_2'] = val elif val_idx == 2: group.loc[idx, 'prev_3'] = val return group
步骤3:应用分组函数并查看结果
把函数应用到分组上,就能得到我们想要的结果:
# 按Param1和Param2分组,应用处理函数 result_df = df.groupby(['Param1', 'Param2'], group_keys=False).apply(get_prev_historical_values) # 查看最终结果 print(result_df[['Param1', 'Param2', 'date', 'value', 'prev_1', 'prev_2', 'prev_3']])
输出结果如下:
Param1 Param2 date value prev_1 prev_2 prev_3 0 a b 2007-10-30 5 None None None 1 a b 2007-10-31 8 None None None 2 a b 2007-11-01 9 None None None 3 a b 2007-12-01 3 None None None 4 a b 2007-12-02 2 None None None 5 a b 2008-03-01 11 2 3 9 6 b c 2008-10-05 7 None None None 7 b c 2008-10-06 13 None None None 8 b c 2008-10-07 19 None None None 9 b c 2008-11-08 22 None None None 10 b c 2008-11-09 35 None None None 11 b c 2008-12-08 5 35 22 19
补充说明
- 为什么不用纯向量化方法?因为要针对每行单独筛选符合时间条件的历史记录,纯向量化实现会非常复杂,可读性极差,分组内的小循环在数据量不是特别巨大的情况下,效率完全够用。
- 这里的
prev_1是间隔至少30天的最近一条历史值,prev_2是次近的,prev_3是第三近的,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Binyamin Even
相关产品推荐
相关产品推荐

