如何基于DataFrameGroupBy对象实现高效滚动时间窗口运算?
高效实现分组时间窗口的未来值判断
针对你提到的大数据集下循环效率低的问题,我们可以通过**聚合+分组滚动(反转时间序列)**的方式实现向量化操作,大幅提升速度。以下是具体步骤和代码:
核心思路
- 先聚合同一
id-date组合的target值:因为同一时间同一ID下只要有一个category的target=1,该时间点就视为存在1,用max()聚合即可。 - 对每个
id分组,反转时间序列:利用pandas滚动窗口默认向前(过去)查看的特性,反转后未来的日期会变成窗口的"过去"部分,方便我们计算未来6个月的情况。 - 滚动窗口计算:使用6个月时间窗口,判断窗口内是否存在
target=1,再反转结果还原顺序。
完整代码
import numpy as np import pandas as pd # 构建原始DataFrame(你的代码) ids = np.concatenate([np.ones(6), np.ones(6)+1]) dates = ['2017-01-01','2017-01-01','2017-01-21','2017-01-21', '2017-10-01','2017-10-01','2017-01-01','2017-01-01', '2017-01-21','2017-01-21','2017-10-01','2017-10-01'] categories = ['a','b','a','b','a','b','a','b','a','b','a','b'] targets = [0,0,1,1,0,0,1,1,0,0,0,0] df = pd.DataFrame({'id':ids, 'date':dates, 'category':categories, 'target':targets}) df['date'] = pd.to_datetime(df['date']) # 步骤1:聚合每个id-date的target最大值(同一时间点只要有1就记为1) df_agg = df.groupby(['id', 'date'])['target'].max().reset_index() # 步骤2:定义分组处理函数,计算未来6个月内是否有1 def check_future_6m(group): # 确保组内按日期升序排列 group_sorted = group.sort_values('date') # 反转时间序列,让未来日期变为"过去" reversed_group = group_sorted.iloc[::-1] # 滚动窗口:6个月,closed='left'排除当前日期(只看未来) reversed_group['one_within_6m'] = reversed_group['target'].rolling( window='6M', on='date', closed='left' ).sum() > 0 # 反转回原顺序 return reversed_group.iloc[::-1] # 步骤3:应用函数到每个id分组,得到结果 df_result = df_agg.groupby('id', group_keys=False).apply(check_future_6m) # 输出预期格式的结果 print(df_result[['id', 'date', 'one_within_6m']])
关键细节解释
- 聚合步骤:使用
groupby(['id', 'date'])['target'].max(),确保同一id-date下只要有一个target=1就保留1,否则为0,避免重复计算。 - 反转时间序列:解决pandas滚动窗口默认只看过去数据的问题,反转后未来的日期会被包含在滚动窗口中。
- 滚动窗口参数:
closed='left'确保窗口不包含当前行(对应原数据的当前日期),完全符合你"不含当前日期,只看未来6个月"的需求;sum() > 0快速判断窗口内是否存在至少一个1。
输出结果
运行代码后会得到和你预期完全一致的结果:
id date one_within_6m 0 1.0 2017-01-01 True 2 1.0 2017-01-21 False 4 1.0 2017-10-01 False 6 2.0 2017-01-01 False 8 2.0 2017-01-21 False 10 2.0 2017-10-01 False
这个方法完全避免了循环,利用pandas的向量化操作,在大数据集下会比循环快几个数量级。
内容的提问来源于stack exchange,提问作者FChm
相关产品推荐
相关产品推荐

