分组依据过滤器,基于连续序列排序以及ID和Date列

时间:2019-07-29 11:23:54

标签: pandas pandas-groupby

我有一个如下所示的数据框

    ID  Status  Date    
0   1   F   2017-06-22  
1   1   M   2017-07-22  
2   1   P   2017-10-22  
3   1   F   2018-06-22 
4   1   P   2018-08-22  
5   1   F   2018-10-22  
6   1   F   2019-03-22  
7   2   M   2017-06-29 
8   2   F   2017-09-29 
9   2   F   2018-01-29  
10  2   M   2018-03-29 
11  2   P   2018-08-29  
12  2   M   2018-10-29  
13  2   F   2018-12-29  
14  3   M   2017-03-20  
15  3   F   2018-06-20  
16  3   P   2018-08-20  
17  3   M   2018-10-20  
18  3   F   2018-11-20  
19  3   P   2018-12-20  
20  3   F   2019-03-20  
22  4   M   2017-08-10  
23  4   F   2018-06-10  
24  4   P   2018-08-10  
25  4   F   2018-12-10  
26  4   M   2019-01-10  
27  4   F   2019-06-10  
31  7   M   2017-08-10  
32  7   F   2018-04-10  
33  7   P   2018-08-10  
34  7   F   2018-11-10  
33  7   P   2019-08-10  
34  7   F   2019-10-10   

我想将每个ID的上方数据帧过滤为连续的F-P-F或F-P-F-P-F,依此类推。

完成上述步骤后,现在我们的数据帧如下所示。

    ID  Status  Date      
3   1   F   2018-06-22 
4   1   P   2018-08-22  
5   1   F   2018-10-22   
18  3   F   2018-11-20  
19  3   P   2018-12-20  
20  3   F   2019-03-20   
23  4   F   2018-06-10  
24  4   P   2018-08-10  
25  4   F   2018-12-10    
32  7   F   2018-04-10  
33  7   P   2018-08-10  
34  7   F   2018-11-10
35  7   P   2019-08-10  
36  7   F   2019-10-10   

然后从上方的数据框中计算出“持续时间”列,如下所示。

df['Duration'] = df.groupby('ID')['Date'].diff().dt.days

    ID  Status  Date    Duration
0   1   F   2018-06-22  nan
1   1   P   2018-08-22  61.00
2   1   F   2018-10-22  61.00
3   3   F   2018-11-20  nan
4   3   P   2018-12-20  30.00
5   3   F   2019-03-20  90.00
6   4   F   2018-06-10  nan
7   4   P   2018-08-10  61.00
8   4   F   2018-12-10  122.00
9   7   F   2018-04-10  nan
10  7   P   2018-08-10  122.00
11  7   F   2018-11-10  92.00
12  7   P   2019-08-10  273.00
13  7   F   2019-10-10  61.00

我想从上面的DF准备下面的数据框。

所以最终的预期输出如下所示

ID   F-P_Duration  F-F_Duration  P-F_Duration
1    61            122           61
3    30            120           30
4    61            183           122
7_1  122           214           92
7_2  273           334           61

2 个答案:

答案 0 :(得分:1)

尝试一下:

import re
import pandas as pd
import numpy as np

df = pd.read_clipboard()
df['Date'] = pd.to_datetime(df['Date'])
print(df)

输出:

    ID Status       Date
0    1      F 2017-06-22
1    1      M 2017-07-22
2    1      P 2017-10-22
3    1      F 2018-06-22
4    1      P 2018-08-22
5    1      F 2018-10-22
6    1      F 2019-03-22
7    2      M 2017-06-29
8    2      F 2017-09-29
9    2      F 2018-01-29
10   2      M 2018-03-29
11   2      P 2018-08-29
12   2      M 2018-10-29
13   2      F 2018-12-29
14   3      M 2017-03-20
15   3      F 2018-06-20
16   3      P 2018-08-20
17   3      M 2018-10-20
18   3      F 2018-11-20
19   3      P 2018-12-20
20   3      F 2019-03-20
22   4      M 2017-08-10
23   4      F 2018-06-10
24   4      P 2018-08-10
25   4      F 2018-12-10
26   4      M 2019-01-10
27   4      F 2019-06-10
31   7      M 2017-08-10
32   7      F 2018-04-10
33   7      P 2018-08-10
34   7      F 2018-11-10
33   7      P 2019-08-10
34   7      F 2019-10-10

我的第一个技巧是使用正则表达式在较长的字符串中查找子字符串的位置。  使用join,我构建了一个字符串并在该字符串中寻找模式。通过将较长的图案放在第一位来定义图案。

pattern = "FPFPF|FPF"
def f(x):
    m = re.search(pattern, ''.join(x['Status']))
    return x[m.start():m.end()] if m else None
df1 = df.groupby('ID', group_keys=False).apply(f)
print(df1)

输出:

    ID Status       Date
3    1      F 2018-06-22
4    1      P 2018-08-22
5    1      F 2018-10-22
18   3      F 2018-11-20
19   3      P 2018-12-20
20   3      F 2019-03-20
23   4      F 2018-06-10
24   4      P 2018-08-10
25   4      F 2018-12-10
32   7      F 2018-04-10
33   7      P 2018-08-10
34   7      F 2018-11-10
33   7      P 2019-08-10
34   7      F 2019-10-10

计算持续时间

df1['Duration'] = df1.groupby('ID')['Date'].diff().dt.days
print(df1)

输出:

    ID Status       Date  Duration
3    1      F 2018-06-22       NaN
4    1      P 2018-08-22      61.0
5    1      F 2018-10-22      61.0
18   3      F 2018-11-20       NaN
19   3      P 2018-12-20      30.0
20   3      F 2019-03-20      90.0
23   4      F 2018-06-10       NaN
24   4      P 2018-08-10      61.0
25   4      F 2018-12-10     122.0
32   7      F 2018-04-10       NaN
33   7      P 2018-08-10     122.0
34   7      F 2018-11-10      92.0
33   7      P 2019-08-10     273.0
34   7      F 2019-10-10      61.0

使用最新的0.25熊猫重新聚合时的聚合:

df_out = df1.groupby(['ID',(df1['Status'] != 'F').cumsum()])['Duration']\
            .agg(F_P_Duration = lambda x: x.iloc[0], 
                 F_F_Duration = 'sum').dropna()
print(df_out)

输出:

           F_P_Duration  F_F_Duration
ID Status                            
1  1               61.0         122.0
3  2               30.0         120.0
4  3               61.0         183.0
7  4              122.0         214.0
   5              273.0         334.0

针对Pandas 0.25之前的版本进行更新...

df1.groupby(['ID',(df1['Status'] != 'F').cumsum()])['Duration']\
            .agg(['first', 'sum', 'last']).dropna()\
            .rename(columns={'first':'F_P_Duration',
                             'sum':'F_F_Duration',
                             'last':'P_F_Duration'})

输出:

           F_P_Duration  F_F_Duration  P_F_Duration
ID Status                                          
1  1               61.0         122.0          61.0
3  2               30.0         120.0          90.0
4  3               61.0         183.0         122.0
7  4              122.0         214.0          92.0
   5              273.0         334.0          61.0

答案 1 :(得分:0)

尝试一下是否适合您

mask=df['Status']=='M' 
dff=df.loc[~mask, :].copy()
dff['a']= dff.groupby(["ID"])['Status'].shift()
df2=dff.loc[dff['Status'] != dff['a']]
df2.drop('a' , axis=1, inplace=True)

否则,将获得与上述相同的结果

mask=df['Status']=='M'
dff=df.loc[~mask, :].copy()
dff= dff.loc[dff.groupby(["ID"])['Status'].shift()!= dff['Status']]