在DataFrameGroupby对象上滚动操作

时间:2019-10-30 15:06:35

标签: python pandas pandas-groupby

我有一个熊猫数据框,希望对数据中的不同组执行相同的滚动操作。考虑下面的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-date对计算一个布尔值,以指示在给定日期的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个月来执行此操作,但是由于我的数据集很大,它太慢了。

所以,我想知道是否可以对日期进行分组,并在时间窗口上进行滚动操作以查看此内容?例如:

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'])

1 个答案:

答案 0 :(得分:0)

我找到了一个可行的解决方案,但只有在每个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())

这里有两个技巧:

  1. 我颠倒日期(.iloc[::-1])的顺序以获取前瞻性窗口;其他SO questions中已建议使用此方法。

  2. 我删除总和的最后一项,以从总和中删除“当前”日期,因此它只是向前看。

第二个“ hack”表示仅当给定id没有重复的日期时才起作用。

我想提出一个更可靠的解决方案(例如,重复输入ID的日期)。