寻找有条件的顺序模式

时间:2019-02-06 17:08:47

标签: python pandas numpy dataframe data-manipulation

我有一个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的列表及其各自的计数(如果可能)。

3 个答案:

答案 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,其值123对应于字符串'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)

我基于以下假设使用了一个解决方案,即ABDCDEB之外的任何事物都不与解决方案相关。因此,我首先通过过滤操作摆脱了它们。

然后,我想知道是否存在ABD,然后是CDE,中间没有B。我将Events列及时移了一个(请注意,以SeqNo为单位,这不必是1步)。

然后,我检查新df的每一列是否为Events==ABD Events_1_Step==CDE,这意味着它们之间没有B,但可能还有其他内容例如AC甚至什么都没有。每当我有这样的序列时,这就会为我提供布尔值列表。如果我总结一下,我就算了。

最后,我必须确保所有操作都在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}的TrueEvent == 'CDE"的{​​{1}}或在新False的开头。通过使用ffill进行向前填充。每个值都有BId在最后。然后,您可以检查值为ABD时是否为True。然后,您可以使用GroupBy来检查每个B是否有CDE

哪个

True

输出:

Id