根据其他列的值划分月份间隔

Divide the interval of months with respect to value from other column

问题:我有一个数据框记录一段时间内客户的状态。对于每个客户(组),我想根据他们在那段时间的状态将时间间隔分为 "Start" 和 "Finish"。

例如,我有这个数据框:

df = pd.DataFrame({'Group': ['group1', 'group1', 'group1', 'group1', 'group1', 'group1', 'group1', 'group1',
                    'group2', 'group2', 'group2', 'group2', 'group2', 'group2', 'group2', 'group2', 'group3'],
               'Month': ['2019-05', '2019-06', '2019-07', '2019-08', '2019-09', '2019-10', '2019-11', '2019-12',
                    '2019-04', '2019-05', '2019-06', '2019-07', '2019-08', '2019-09', '2019-10', '2019-11', '2019-12'],
               'Status': ['Passive', 'Passive', 'Passive', 'Active', 'Active', 'Active', 'Passive', 'Passive',
                    'Active', 'Active', 'Passive', 'Passive', 'Passive', 'Active', 'Active', 'Active', 'Active']})

我想把它转成这个结构:

df_new = pd.DataFrame({'Group': ['group1', 'group1', 'group1', 'group2', 'group2', 'group2', 'group3'],
                   'From': ['2019-05', '2019-08', '2019-11', '2019-04', '2019-06', '2019-09', '2019-12'],
                   'To': ['2019-07', '2019-10', '2019-12', '2019-05', '2019-08', '2019-11', '2019-12'],
                   'Status': ['Passive', 'Active', 'Passive', 'Active', 'Passive', 'Active', 'Active']})

如果没有 "Status" 变量,使用 groupby 和 aggfunc 在每个组中查找 "min" 和 "max" 周期将非常简单。但是,我不知道如何考虑 "Status" 变量。问题是这里的状态间隔不是连续的,所以如果我按 "Status" 分组,我总是只有 2 个状态组(主动和被动)并且间隔是混合的。

我正在考虑将数据帧分成 2 个数据帧:一个状态为 "active",一个状态为 "passive";分别在这两个上工作,然后再次合并在一起。但是这种方法似乎不是那么有效:(而且由于一个客户可以多次主动和被动,因此在每个状态组内划分间隔非常棘手。

有没有更好的解决方案?

使用 groupby by Group and by a helper Series of contiguous Status. With pandas v 0.25.0+ you can use named aggregationsminmax 用于 "From","to" 和 last 用于 'Status':

s = df['Status'].ne(df['Status'].shift()).cumsum()

df_new = (df.groupby(['Group', s])
          .agg(From=('Month', 'min'),
               To=('Month', 'max'),
               Status=('Status', 'last'))
           .reset_index(level=0))

[出]

         Group     From       To   Status
Status                                   
1       group1  2019-05  2019-07  Passive
2       group1  2019-08  2019-10   Active
3       group1  2019-11  2019-12  Passive
4       group2  2019-04  2019-05   Active
5       group2  2019-06  2019-08  Passive
6       group2  2019-09  2019-11   Active
6       group3  2019-12  2019-12   Active

正如@nhupn 指出的那样,如果使用旧版本的 pandas,聚合可以通过以下方式完成:

df_new = (df.groupby(['Group', s])
          .agg({'Month': [('From', 'min'),
                          ('To', 'max')],
                'Status': [('Status', 'last')]})
          .reset_index(level=0))