我有一个pandas数据帧,我希望对数据中的不同组执行相同的滚动操作。考虑以下具有四列的df
(有关要构建的代码,请参阅问题底部(:
id date category target
1 2017-01-01 'a' 0
1 2017-01-01 'b' 0
1 2017-01-21 'a' 1
1 2017-01-21 'b' 1
1 2017-10-01 'a' 0
1 2017-10-01 'b' 0
2 2017-01-01 'a' 1
2 2017-01-01 'b' 1
2 2017-01-21 'a' 0
2 2017-01-21 'b' 0
2 2017-10-01 'a' 0
2 2017-10-01 'b' 0
我想要的是一个操作,它为每个唯一的id-日期对计算一个布尔值,指示目标列在给定日期的6个月内是否为1。因此,对于所提供的df,我希望得到这样的结果:
id date one_within_6m
1 2017-01-01 True
1 2017-01-21 False
1 2017-10-01 False
2 2017-01-01 False
2 2017-01-21 False
2 2017-10-01 False
我可以使用for循环来迭代行,并提前6个月查找每次访问,但由于我的数据集太大,速度太慢。
所以,我想知道是否有可能按id对日期进行分组,并在时间窗口上进行滚动操作来查看这一点?例如:
df_grouped = df.groupby(['id', 'date'])
# … do something to set date as index
# ... define some custom function
df_grouped.rolling('6m', on='target').apply(some_custom_function)
一些注意事项:
在6个月的窗口中可以有多个"1",对于当前日期,这应该被视为True。
在我的脑海中,
some_custom_function
将检查未来6个月(不包括当前日期(的目标总和是否大于1。
支持代码:
生成此问题中使用的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'])
我找到了一个可行的解决方案,但它只有在每个id的每个日期都是唯一的情况下才有效。这是我的数据中的情况,需要一些额外的处理:
new_df = df.groupby(['id','date']).mean().reset_index()
返回:
id date target
0 1.0 2017-01-01 0
1 1.0 2017-01-21 1
2 1.0 2017-10-01 0
3 2.0 2017-01-01 1
4 2.0 2017-01-21 0
5 2.0 2017-10-01 0
然后,我可以在groupby对象上使用滚动方法来获得所需的结果:
df = new_df.set_index('date')
df.iloc[::-1].groupby('id')['target'].rolling(window='180D',
centre=False).apply(lambda x : x[:-1].sum())
这里有两个技巧:
我颠倒日期的顺序(
.iloc[::-1]
(以获得前瞻性窗口;其他SO问题中也提出了这一点。我去掉了总和的最后一个条目,从总和中删除了"当前"日期,所以它只向前看。
第二个"破解"意味着它只有在给定id没有重复日期时才有效。
我有兴趣制作一个更健壮的解决方案(例如,重复id的日期(。