与这个问题有关
我有一个数据帧,我想为每个id行,直到状态是1的第一次。在上面的线程中提出的解决方案工作完美,但速度很慢。我的数据集中有70,000行。
我的数据集:
d = {'id': [1,1,1,1,1,1,1,2,2,2,2,2,2,2], 'status': [0,0,0,0,1,1,1,0,0,0,0,1,0,1]}
df = pd.DataFrame(data=d)
id status
0 1 0
1 1 0
2 1 0
3 1 0
4 1 1
5 1 1
6 1 1
7 2 0
8 2 0
9 2 0
10 2 0
11 2 1
12 2 0
13 2 1
我想要的子集是:
id status
0 1 0
1 1 0
2 1 0
3 1 0
4 1 1
5 2 0
6 2 0
7 2 0
8 2 0
9 2 1
在相关线程中尝试解决方案:
lambda x: x.cumsum().cumsum().le(1)
可以,但是速度很慢。
您可以在此线程中使用parallel-pandas并行化解决方案。下面是一个用法示例
import pandas as pd
#pip install parallel-pandas
from parallel_pandas import ParallelPandas
#initialize parallel-pandas
ParallelPandas.initialize(n_cpu=8)
#p_apply is parallel analogue of apply method
df = df.groupby('id', group_keys=False)
.p_apply(lambda x: x[x.status.cumsum().cumsum().le(1)])
.reset_index(drop=1)
您可以尝试:
df[~df.groupby('id', group_keys=False)['status'].apply(lambda s: s.shift().eq(1).cummax())]
获得cummax
可能比获得cumsum
更快
或者,假设只有0/1的值:
df.groupby('id', group_keys=False).apply(lambda g: g.loc[:g['status'].idxmax()])
这里有一个可能的解决方案。这当然可以改进,但它是有效的。
df.groupby("id")['status'].apply(lambda x: x[:x.values.tolist().index(1) + 1]).swaplevel()
解释:
.apply(lambda x: x[:x.values.tolist().index(1) + 1])
这一行由两个主要部分组成。
我们寻找第一次出现的值1,得到它的索引x.values.tolist().index(1)
然后加1,这样就可以包含我们找到的数字了。我们把所有东西从头到尾切成薄片。
对每个id组执行此操作。
/p>这个版本处理在组值中找不到1的情况:def magic(x):
values = x.values.tolist()
if 1 in values:
return x[:values.index(1) + 1]
else:
return x
df.groupby("id")['status'].apply(magic)