Pandas 事件序列的动态滚动计数

Pandas dynamic rolling count of sequence of events

我有以下虚拟数据框:

import pandas as pd
import numpy as np

def random_dates(start, end, n, freq, seed=None):
    if seed is not None:
        np.random.seed(seed)

    dr = pd.date_range(start, end, freq=freq)
    return pd.to_datetime(np.sort(np.random.choice(dr, n, replace=False)))

data = {'Timestamp': random_dates('2018-01-01', '2018-01-02', 21, 'H', seed=[3, 1415]), 
        'Group': [1,1,1,1,1,1,1,1,1,1,1,1,
                 2,2,2,2,2,2,2,2,2],
        
        'Event': ['A','A','A','B','A','A','A','B','A','A','A','B',
                'A','A','B','A','A','B','A','A','B']}

df = pd.DataFrame(data, columns = ['Timestamp', 'Group', 'Event'])
print(df)

             Timestamp  Group Event
0  2018-01-01 00:00:00      1    A
1  2018-01-01 01:00:00      1    A
2  2018-01-01 03:00:00      1    A
3  2018-01-01 04:00:00      1    B
4  2018-01-01 05:00:00      1    A
5  2018-01-01 06:00:00      1    A
6  2018-01-01 07:00:00      1    A
7  2018-01-01 08:00:00      1    B
8  2018-01-01 09:00:00      1    A
9  2018-01-01 12:00:00      1    A
10 2018-01-01 13:00:00      1    A
11 2018-01-01 14:00:00      1    B
12 2018-01-01 15:00:00      2    A
13 2018-01-01 17:00:00      2    A
14 2018-01-01 18:00:00      2    B
15 2018-01-01 19:00:00      2    A
16 2018-01-01 20:00:00      2    A
17 2018-01-01 21:00:00      2    B
18 2018-01-01 22:00:00      2    A
19 2018-01-01 23:00:00      2    A
20 2018-01-02 00:00:00      2    B

我想要每个 'Group' 列 'Event' 的动态滚动计数。可以看出,例如 df['Group']==1 具有事件序列:

A, A, A, B

其中事件序列每三分之一发生一次,因此序列为 3、1。而 df['Group']==2 是:

A, A, B

其中事件序列每秒发生一次,因此序列为 2、1。理想情况下我会:

Group Event Sequence 
1      A      3      
1      B      1      
1      A      3      
1      B      1      
1      A      3      
1      B      1      
2      A      2      
2      B      1      
2      A      2      
2      B      1      
2      A      2      
2      B      1      

这样我就可以绘制Sequence以便进行监控。通过 'dynamic',正如所展示的,Event 的发生正在发生变化,即使在一个 Group 内也是如此!例如 df['Group']==1 也可以看到 3, 1, 3, 1, 2, 1。

用每个事件序列计算经过的时间也很棒。这可以计算为每个组的每个序列的最后一个和第一个事件时间戳之间的差异,我们将有:

Group Event Sequence ElapsedTime
1      A      3      4
1      B      1      1
1      A      3      3
1      B      1      1
1      A      3      5
1      B      1      1
2      A      2      3
2      B      1      1
2      A      2      2
2      B      1      1
2      A      2      2
2      B      1      None

此处第 1 组 'Event' A 的第一个序列的第一行的 ElapsedTime 计算为:

df[df['Group']==1]['Timestamp'].iloc[2] - df[df['Group']==1]['Timestamp'].iloc[0]

组 1 中 'Event' B 的第一个序列的第二行计算为:

df[df['Group']==1]['Timestamp'].iloc[3] - df[df['Group']==1]['Timestamp'].iloc[2]

我在pandas中尝试了rolling count、resample、groupby.cumcount()等方法,其中none返回了我感兴趣的结果。我确信这些方法以 groupby 方式的复杂组合应该可以完成这项工作(至少对于第一种情况),但我已经花了足够的时间尝试和搜索,到目前为止没有成功。这超出了我目前的知识 pandas!

再次感谢您的宝贵时间和经验。

好的。我没有放弃!事实证明,我还需要 1-2 小时才能找到解决方案(很大程度上受到 @DSM 的 this answer 的启发),我学到了很多东西,这里是为那些可能遇到这种数据转换的人准备的,包括经过时间:

df['Lag'] = df['Timestamp'].shift(-1) 
df['Seq'] = df["Event"].groupby((df.Event != df.Event.shift()).cumsum()).transform('size')
df['SeqID'] = (df.Event != df.Event.shift()).cumsum()

df_grp = df.groupby(['Group','SeqID']).first().reset_index()
df_grp['Elapsed(min)'] =  (df.groupby(['Group','SeqID'])['Lag'].last() - df.groupby(['Group','SeqID'])['Timestamp'].first()).reset_index()[0]/ np.timedelta64(1, 'm')

df_grp = df_grp.drop(['Timestamp','Lag'],axis=1)
print(df_grp)


    Group  SeqID Event  Seq  Elapsed(min)
0       1      1     A    3         240.0
1       1      2     B    1          60.0
2       1      3     A    3         180.0
3       1      4     B    1          60.0
4       1      5     A    3         300.0
5       1      6     B    1          60.0
6       2      7     A    2         180.0
7       2      8     B    1          60.0
8       2      9     A    2         120.0
9       2     10     B    1          60.0
10      2     11     A    2         120.0
11      2     12     B    1           NaN

Pandas-ing 快乐! ;)