熊猫 - 选择一对连续的行匹配条件

use*_*984 6 python math pandas

我有一个看起来像这样的数据框

>>> a_df
    state
1    A
2    B
3    A
4    B
5    C
Run Code Online (Sandbox Code Playgroud)

我想做的是返回匹配某个序列的所有连续行。例如,如果此序列为['A', 'B'],则应返回状态A后紧跟 a的行B。在上面的例子中:

>>> cons_criteria(a_df, ['A', 'B'])
    state
1    A
2    B
3    A
4    B
Run Code Online (Sandbox Code Playgroud)

或者如果选择的数组是['A', 'B', 'C'],那么输出应该是

>>> cons_criteria(a_df, ['A', 'B', 'C'])
    state
3    A
4    B
5    C
Run Code Online (Sandbox Code Playgroud)

我决定通过存储当前状态以及下一个状态来做到这一点:

>>> df2 = a_df.copy()
>>> df2['state_0'] = a_df['state']
>>> df2['state_1'] = a_df['state'].shift(-1)
Run Code Online (Sandbox Code Playgroud)

现在,我可以匹配state_0state_1。但这只会返回第一个条目:

>>> df2[(df2['state_0'] == 'A') & (df2['state_1'] == 'B')]
    state
1    A
3    A
Run Code Online (Sandbox Code Playgroud)

我应该如何修复这里的逻辑以便返回所有连续的行?在熊猫中有没有更好的方法来解决这个问题?

piR*_*red 5

我会使用这样的功能

def match_slc(s, seq):
    # get list, makes zip faster
    l = s.values.tolist()
    # count how many in sequence
    k = len(seq)
    # generate numpy array of rolling values
    a = np.array(list(zip(*[l[i:] for i in range(k)])))
    # slice an array from 0 to length of a - 1 with 
    # the truth values of wether all 3 in a sequence match
    p = np.arange(len(a))[(a == seq).all(1)]
    # p tracks the beginning of a match, get all subsequent
    # indices of the match as well.
    slc = np.unique(np.hstack([p + i for i in range(k)]))
    return s.iloc[slc]
Run Code Online (Sandbox Code Playgroud)

示范

s = pd.Series(list('ABABC'))

print(match_slc(s, list('ABC')), '\n')
print(match_slc(s, list('AB')), '\n')

2    A
3    B
4    C
dtype: object 

0    A
1    B
2    A
3    B
dtype: object 
Run Code Online (Sandbox Code Playgroud)


DYZ*_*DYZ 3

这是一个适合我的解决方案 - 但仅适用于数字行索引。我让你的数据框变得更有趣了,现在它有 2 个 ABC 模式:

a_df=pd.DataFrame(['A','B','A','B','C','D','A','A','B','C','E'],
                  columns=["state"])
Run Code Online (Sandbox Code Playgroud)

这是匹配模式:

pattern = ['A','B','C']
Run Code Online (Sandbox Code Playgroud)

此表达式查找每个模式的一组起始行:

starts = set(a_df[a_df['state']          =='A'].index) & 
         set(a_df[a_df['state'].shift(-1)=='B'].index) & 
         set(a_df[a_df['state'].shift(-2)=='C'].index)
print(starts)
# {2, 7}
Run Code Online (Sandbox Code Playgroud)

一般来说:

starts = set.intersection(
           *[set(a_df[a_df['state'].shift(-i)==value].index) 
             for i,value in enumerate(pattern)])
Run Code Online (Sandbox Code Playgroud)

此表达式将起始行号转换为 3 行范围并选择行范围:

result = [a_df.ix[range(i, i+3)] for i in starts]
print(result)
# [  state
# 2     A
# 3     B
# 4     C,   state
# 7     A
# 8     B
# 9     C]
Run Code Online (Sandbox Code Playgroud)

一般来说:

result = [a_df.ix[range(i, i+len(pattern))] for i in starts]
Run Code Online (Sandbox Code Playgroud)