我有一个看起来像这样的数据框:
id date isActive
0 1 2019-01-01 0
1 1 2019-01-02 1
2 1 2019-01-03 1
3 1 2019-01-04 0
4 1 2019-01-05 0
5 2 2019-01-01 0
6 2 2019-01-02 1
7 2 2019-01-03 0
8 2 2019-01-04 1
9 2 2019-01-05 0
我想过滤出每个ID的所有无效(isActive = 0)行,除非该ID的最新行无效。然后,我的数据框应如下所示:
id date isActive
0 1 2019-01-02 1
1 1 2019-01-03 1
2 1 2019-01-04 0
3 1 2019-01-05 0
4 2 2019-01-02 1
5 2 2019-01-04 1
6 2 2019-01-05 0
我以为我应该尝试保留所有活动行,以及与每个ID的最后一组连续isActive值关联的行。为此,我尝试创建一个标志来指示isActive变量何时更改,然后尝试获取每个组的大小:
df['flag'] = df.groupby(['id', df['isActive'].eq(1).cumsum()])['isActive'].transform('size')
然后,我尝试使用apply
和tail
来保持满足上述条件的每一行,但我意识到我不能只访问最后一列的flag值:
df.groupby(['ID']).apply(lambda x: (x['Status'].eq(2)) | (x['Status'].tail(x['flag'])))
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
我想我可能会考虑这个问题。有没有更好的方法?
检查isActive
是否为0,并从组内的底部获取累积乘积。这将为您提供最后一个0条纹(如果有)的True
。将其与1
的所有行合并,即可获得总掩码。
# Assumes sorted by date within each id
m = (df['isActive'].eq(0)[::-1].groupby(df['id']).cumprod().sort_index()
| df['isActive'].eq(1))
df[m]
id date isActive
1 1 2019-01-02 1
2 1 2019-01-03 1
3 1 2019-01-04 0
4 1 2019-01-05 0
6 2 2019-01-02 1
8 2 2019-01-04 1
9 2 2019-01-05 0
使用pyjanitor将转换挂接到数据帧,使用shift函数从下一行获取值,并过滤掉任何等于-1的值,因为0-1为负数,但0-0或1-1不会。
import pandas as pd
import pyjanitor
(df.groupby_agg(by='id',
agg=lambda x: x - x.shift(-1),
agg_column_name='isActive',
new_column_name='cumu'
)
.fillna(0)
.query('cumu != -1')
.reset_index(drop=True)
.drop('cumu',axis=1)
)
id date isActive
0 1 2019-01-02 1
1 1 2019-01-03 1
2 1 2019-01-04 0
3 1 2019-01-05 0
4 2 2019-01-02 1
5 2 2019-01-04 1
6 2 2019-01-05 0