首页 > 解决方案 > Groupby 根据计数过滤,计算持续时间,倒数第二个状态

问题描述

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

   ID   Status  Date        Cost
0   1   F       2017-06-22  500
1   1   M       2017-07-22  100
2   1   P       2017-10-22  100
3   1   F       2018-06-22  600
4   1   M       2018-08-22  150
5   1   P       2018-10-22  120
6   1   F       2019-03-22  750
7   2   M       2017-06-29  200
8   2   F       2017-09-29  600
9   2   F       2018-01-29  500
10  2   M       2018-03-29  100
11  2   P       2018-08-29  100
12  2   M       2018-10-29  100
13  2   F       2018-12-29  500
14  3   M       2017-03-20  300
15  3   F       2018-06-20  700
16  3   P       2018-08-20  100
17  3   M       2018-10-20  250
18  3   F       2018-11-20  100
19  3   P       2018-12-20  100
20  3   F       2019-03-20  600
21  3   M       2019-05-20  200
22  4   M       2017-08-10  800
23  4   F       2018-06-10  100
24  4   P       2018-08-10  120
25  4   F       2018-10-10  500
26  4   M       2019-01-10  200
27  4   F       2019-06-10  600
28  5   M       2018-10-10  200
29  5   F       2019-06-10  500
30  6   F       2019-06-10  600
31  7   M       2017-08-10  800
32  7   F       2018-06-10  100
33  7   P       2018-08-10  120
34  7   M       2019-01-10  200
35  7   F       2019-06-10  600    

在哪里

F = 故障 M = 维护 P = 计划

Step1 - 选择在最后一次失败之前至少具有两种状态(F或M或P)的ID的数据

Step2 - 如果每个 ID 的最后一个原始数据不是 F,则忽略行,此后的预期输出如下所示。

        ID  Status  Date    Cost
    0   1   F   2017-06-22  500
    1   1   M   2017-07-22  100
    2   1   P   2017-10-22  100
    3   1   F   2018-06-22  600
    4   1   M   2018-08-22  150
    5   1   P   2018-10-22  120
    6   1   F   2019-03-22  750
    7   2   M   2017-06-29  200
    8   2   F   2017-09-29  600
    9   2   F   2018-01-29  500
    10  2   M   2018-03-29  100
    11  2   P   2018-08-29  100
    12  2   M   2018-10-29  100
    13  2   F   2018-12-29  500
    14  3   M   2017-03-20  300
    15  3   F   2018-06-20  700
    16  3   P   2018-08-20  100
    17  3   M   2018-10-20  250
    18  3   F   2018-11-20  100
    19  3   P   2018-12-20  100
    20  3   F   2019-03-20  600
    22  4   M   2017-08-10  800
    23  4   F   2018-06-10  100
    24  4   P   2018-08-10  120
    25  4   F   2018-10-10  500
    26  4   M   2019-01-10  200
    27  4   F   2019-06-10  600
    31  7   M   2017-08-10  800
    32  7   F   2018-06-10  100
    33  7   P   2018-08-10  120
    34  7   M   2019-01-10  200
    35  7   F   2019-06-10  600

现在,对于每个 id 的最后状态是失败

然后从上面的df我想准备下面的数据框

ID  No_of_F  No_of_M  No_of_P  SLS  NoDays_to_SLS NoDays_SLS_to_LS
1   3        2        2        P    487           151
2   3        3        2        M    487           61
3   3        2        2        P    640           90
4   3        1        1        M    518           151
7   2        1        1        M    518           151

SLS = 倒数第二个状态

LS = 最后状态

我尝试了以下代码来计算持续时间。

df['Date'] = pd.to_datetime(df['Date'])
df = df.sort_values(['ID', 'Date', 'Status'])
df['D'] = df.groupby('ID')['Date'].diff().dt.days

标签: pandaspandas-groupby

解决方案


gropuby我们可以使用+创建一个蒙版bfill,允许我们执行两个选择。

m = df.Status.eq('F').replace(False, np.NaN).groupby(df.ID).bfill()
df = df.loc[m.groupby(df.ID).transform('sum').gt(2) & m]

    ID Status        Date  Cost
0    1      F  2017-06-22   500
1    1      M  2017-07-22   100
2    1      P  2017-10-22   100
3    1      F  2018-06-22   600
4    1      M  2018-08-22   150
5    1      P  2018-10-22   120
6    1      F  2019-03-22   750
7    2      M  2017-06-29   200
8    2      F  2017-09-29   600
9    2      F  2018-01-29   500
10   2      M  2018-03-29   100
11   2      P  2018-08-29   100
12   2      M  2018-10-29   100
13   2      F  2018-12-29   500
14   3      M  2017-03-20   300
15   3      F  2018-06-20   700
16   3      P  2018-08-20   100
17   3      M  2018-10-20   250
18   3      F  2018-11-20   100
19   3      P  2018-12-20   100
20   3      F  2019-03-20   600
22   4      M  2017-08-10   800
23   4      F  2018-06-10   100
24   4      P  2018-08-10   120
25   4      F  2018-10-10   500
26   4      M  2019-01-10   200
27   4      F  2019-06-10   600
31   7      M  2017-08-10   800
32   7      F  2018-06-10   100
33   7      P  2018-08-10   120
34   7      M  2019-01-10   200
35   7      F  2019-06-10   600

第二部分有点烦人。几乎可以肯定有一种更聪明的方法可以做到这一点,但这是直截了当的方法:

s = df.Date.diff().dt.days

res = pd.concat([df.groupby('ID').Status.value_counts().unstack().add_prefix('No_of_'),
                 df.groupby('ID').Status.apply(lambda x: x.iloc[-2]).to_frame('SLS'),
                 (s.where(s.gt(0)).groupby(df.ID).apply(lambda x: x.cumsum().iloc[-2])
                   .to_frame('NoDays_to_SLS')),
                 s.groupby(df.ID).apply(lambda x: x.iloc[-1]).to_frame('NoDays_SLS_to_LS')],
                axis=1)

输出:

    No_of_F  No_of_M  No_of_P SLS  NoDays_to_SLS  NoDays_SLS_to_LS
ID                                                                
1         3        2        2   P          487.0             151.0
2         3        3        1   M          487.0              61.0
3         3        2        2   P          640.0              90.0
4         3        2        1   M          518.0             151.0
7         2        2        1   M          518.0             151.0

推荐阅读