在状态改变时记录差异 - 熊猫

时间:2013-01-07 16:13:46

标签: python pandas

我有id,时间戳和状态的客户记录。

ID, TS, STATUS
1 10 GOOD
1 20 GOOD
1 25 BAD
1 30 BAD
1 50 BAD
1 600 GOOD
2 40 GOOD
.. ... 

我试图计算每个客户在连续BAD状态(让我们想象上面的顺序是正确的)上花费了多少时间。因此,对于客户id = 1,在BAD状态下花费了总共575秒的30-25,50-30,600-50。

在熊猫中这样做的方法是什么?如果我在TS上计算.diff(),这会给我带来差异,但我怎样才能将1)与客户联系2)某些状态"阻止"对那个客户?

示例数据:

df = pandas.DataFrame({'ID':[1,1,1,1,1,1,2],
                       'TS':[10,20,25,30,50,600,40],
                       'Status':['G','G','B','B','B','G','G']
                       },
                      columns=['ID','TS','Status'])

谢谢,

2 个答案:

答案 0 :(得分:2)

In [1]: df = DataFrame({'ID':[1,1,1,1,1,2,2],'TS':[10,20,25,30,50,10,40],'Stat
us':['G','G','B','B','B','B','B']}, columns=['ID','TS','Status'])

In [2]: f = lambda x: x.diff().sum()

In [3]: df['diff'] = df[df.Status=='B'].groupby('ID')['TS'].transform(f)

In [4]: df
Out[4]:
   ID  TS Status  diff
0   1  10      G   NaN
1   1  20      G   NaN
2   1  25      B    25
3   1  30      B    25
4   1  50      B    25
5   2  10      B    30
6   2  40      B    30

说明: 将dataframe子集仅设置为具有所需状态的记录。 Groupby ID并将lambda函数diff().sum()应用于每个组。使用transform代替apply,因为transform会返回一个可用于指定新列'diff'的索引系列。

编辑:对扩大的问题范围进行新的回复。

In [1]: df
Out[1]:
   ID   TS Status
0   1   10      G
1   1   20      G
2   1   25      B
3   1   30      B
4   1   50      B
5   1  600      G
6   2   40      G

In [2]: df['shift'] = -df['TS'].diff(-1)

In [3]: df['diff'] = df[df.Status=='B'].groupby('ID')['shift'].transform('sum')
In [4]: df
Out[4]:
   ID   TS Status  shift  diff
0   1   10      G     10   NaN
1   1   20      G      5   NaN
2   1   25      B      5   575
3   1   30      B     20   575
4   1   50      B    550   575
5   1  600      G   -560   NaN
6   2   40      G    NaN   NaN

答案 1 :(得分:2)

这是一个单独汇总每个连续不良状态块的解决方案(问题的第2部分?)。

In [5]: df = pandas.DataFrame({'ID':[1,1,1,1,1,1,1,1,2,2,2],
                               'TS':[10,20,25,30,50,600,650,670,40,50,60],
                               'Status':['G','G','B','B','B','G','B','B','G','B','B']
                               },
                               columns=['ID','TS','Status'])

In [6]: grp = df.groupby('ID')

In [7]: def status_change(df):
   ...:         return (df.Status.shift(1) != df.Status).astype(int)
   ...: 

In [8]: df['BlockId'] = grp.apply(lambda df: status_change(df).cumsum())

In [9]: df['Duration'] = grp.TS.diff().shift(-1)

In [10]: df
Out[10]: 
    ID   TS Status  BlockId  Duration
0    1   10      G        1        10
1    1   20      G        1         5
2    1   25      B        2         5
3    1   30      B        2        20
4    1   50      B        2       550
5    1  600      G        3        50
6    1  650      B        4        20
7    1  670      B        4       NaN
8    2   40      G        1        10
9    2   50      B        2        10
10   2   60      B        2       NaN

In [11]: df[df.Status == 'B'].groupby(['ID', 'BlockId']).Duration.sum()
Out[11]: 
ID  BlockId
1   2          575
    4           20
2   2           10
Name: Duration