我有一个df
Id Event SeqNo
1 A 1
1 B 2
1 C 3
1 ABD 4
1 A 5
1 C 6
1 A 7
1 CDE 8
1 D 9
1 B 10
1 ABD 11
1 D 12
1 B 13
1 CDE 14
1 A 15
我正在寻找一种模式“ ABD后跟CDE,而它们之间没有事件B” 例如,此df的输出将为:
Id Event SeqNo
1 ABD 4
1 A 5
1 C 6
1 A 7
1 CDE 8
单个ID可以多次遵循此模式,我想找到所有ID的列表及其各自的计数(如果可能)。
答案 0 :(得分:2)
这是一个矢量化的矢量,具有一些缩放技巧,并利用卷积来找到所需的模式-
# Get the col in context and scale it to the three strings to form an ID array
a = df['Event']
id_ar = (a=='ABD') + 2*(a=='B') + 3*(a=='CDE')
# Mask of those specific strings and hence extract the corresponding masked df
mask = id_ar>0
df1 = df[mask]
# Get pattern col with 1s at places with the pattern found, 0s elsewhere
df1['Pattern'] = (np.convolve(id_ar[mask],[9,1],'same')==28).astype(int)
# Groupby Id col and sum the pattern col for final output
out = df1.groupby(['Id'])['Pattern'].sum()
convolution
部分可能有些棘手。此处的想法是使用id_ar
,其值1
,2
和3
对应于字符串'ABD'
,''B'
和{{ 1}}。我们正在寻找'CDE'
后跟1
,因此将卷积与内核3
一起使用将导致[9,1]
作为具有{{1}的窗口的卷积和},然后1*1 + 3*9 = 28
。因此,我们寻找转化。 'ABD'
的总和。对于'CDE'
然后是'28
然后是'ABD'
的情况,请进行转化。总和会有所不同,因此将被过滤掉。
样品运行-
1)输入数据框:
'B'
2)中间过滤的o / p(请查看列'CDE'
中是否存在必需的模式):
In [377]: df
Out[377]:
Id Event SeqNo
0 1 A 1
1 1 B 2
2 1 C 3
3 1 ABD 4
4 1 B 5
5 1 C 6
6 1 A 7
7 1 CDE 8
8 1 D 9
9 1 B 10
10 1 ABD 11
11 1 D 12
12 1 B 13
13 2 A 1
14 2 B 2
15 2 C 3
16 2 ABD 4
17 2 A 5
18 2 C 6
19 2 A 7
20 2 CDE 8
21 2 D 9
22 2 B 10
23 2 ABD 11
24 2 D 12
25 2 B 13
26 2 CDE 14
27 2 A 15
3)最终o / p:
Pattern
答案 1 :(得分:1)
我基于以下假设使用了一个解决方案,即ABD
,CDE
和B
之外的任何事物都不与解决方案相关。因此,我首先通过过滤操作摆脱了它们。
然后,我想知道是否存在ABD,然后是CDE,中间没有B。我将Events
列及时移了一个(请注意,以SeqNo
为单位,这不必是1步)。
然后,我检查新df的每一列是否为Events==ABD
和 Events_1_Step==CDE
,这意味着它们之间没有B
,但可能还有其他内容例如A
或C
甚至什么都没有。每当我有这样的序列时,这就会为我提供布尔值列表。如果我总结一下,我就算了。
最后,我必须确保所有操作都在Id
级别完成,因此请使用.groupby
。
重要提示::该解决方案假定您的df首先按Id
排序,然后按SeqNo
排序。如果没有,请这样做。
import pandas as pd
df = pd.read_csv("path/to/file.csv")
df2 = df[df["Event"].isin(["ABD", "CDE", "B"])]
df2.loc[:,"Event_1_Step"] = df2["Event"].shift(-1)
df2.loc[:,"SeqNo_1_Step"] = df2["SeqNo"].shift(-1)
for id, id_df in df2.groupby("Id"):
print(id) # Set a counter object here per Id to track count per id
id_df = id_df[id_df.apply(lambda x: x["Event"] == "ABD" and x["Event_1_Step"] == "CDE", axis=1)]
for row_id, row in id_df.iterrows():
print(df[(df["Id"] == id) * df["SeqNo"].between(row["SeqNo"], row["SeqNo_1_Step"])])
答案 2 :(得分:-1)
您可以使用此:
s = (pd.Series(
np.select([df['Event'] == 'ABD', df['Event'] =='B', df['Id'] != df['Id'].shift()],
[True, False, False], default=np.nan))
.ffill()
.fillna(False)
.astype(bool))
corr = (df['Event'] == "CDE") & s
corr.groupby(df['Id']).max()
使用np.select
创建一个列,该列包含{{1}的True
和Event == 'CDE"
的{{1}}或在新False
的开头。通过使用ffill
进行向前填充。每个值都有B
或Id
在最后。然后,您可以检查值为ABD
时是否为True。然后,您可以使用GroupBy
来检查每个B
是否有CDE
。
哪个
True
输出:
Id