Pandas - 查找和索引与行序列模式匹配的行

标签 pandas indexing row

我想在向下行的分类变量中的数据框中找到模式。我可以看到如何使用 Series.shift() 向上/向下查找并使用 bool 逻辑来查找模式,但是,我想使用分组变量来执行此操作,并标记属于该模式的所有行,而不仅仅是起始行。

代码:

import pandas as pd
from numpy.random import choice, randn
import string

# df constructor
n_rows = 1000
df = pd.DataFrame({'date_time': pd.date_range('2/9/2018', periods=n_rows, freq='H'),
                   'group_var': choice(list(string.ascii_uppercase), n_rows),
                   'row_pat': choice([0, 1, 2, 3], n_rows),
                   'values': randn(n_rows)})

# sorting 
df.sort_values(by=['group_var', 'date_time'], inplace=True)
df.head(10)

返回这个:enter image description here

我可以通过以下方式找到模式的开始(虽然没有分组):
# the row ordinal pattern to detect
p0, p1, p2, p3 = 1, 2, 2, 0 

# flag the row at the start of the pattern
df['pat_flag'] = \
df['row_pat'].eq(p0) & \
df['row_pat'].shift(-1).eq(p1) & \
df['row_pat'].shift(-2).eq(p2) & \
df['row_pat'].shift(-3).eq(p3)

df.head(10)

enter image description here

我无法弄清楚的是,如何仅使用“group_var”来执行此操作,而不是在模式的开头返回 True,而是为属于该模式的所有行返回 true。

感谢有关如何解决此问题的任何提示!

谢谢...

最佳答案

我认为您有两种方法 - 更简单和更慢的解决方案或更快的复杂解决方案。

  • 使用 Rolling.apply 和测试模式
  • 0 s 替换为 NaN s 由 mask
  • 使用 bfilllimit(与 fillna method='bfill' 相同)重复 1
  • 然后 fillna NaN s 到 0
  • 最后一次由 astype 转换为 bool

  • pat = np.asarray([1, 2, 2, 0])
    N = len(pat)
    
    
    df['rm0'] = (df['row_pat'].rolling(window=N , min_periods=N)
                              .apply(lambda x: (x==pat).all())
                              .mask(lambda x: x == 0) 
                              .bfill(limit=N-1)
                              .fillna(0)
                              .astype(bool)
                 )
    

    如果是重要的性能,请使用 strides ,修改 link 的解决方案:
  • 使用 rolling window 方法
  • 与模式比较并返回 True s 以匹配 all
  • 通过 np.mgrid 和索引
  • 获取首次出现的索引
  • 使用列表理解创建所有索引
  • 通过 numpy.in1d 进行比较并创建新列

  • def rolling_window(a, window):
        shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
        strides = a.strides + (a.strides[-1],)
        c = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
        return c
    
    arr = df['row_pat'].values
    b = np.all(rolling_window(arr, N) == pat, axis=1)
    c = np.mgrid[0:len(b)][b]
    
    d = [i  for x in c for i in range(x, x+N)]
    df['rm2'] = np.in1d(np.arange(len(arr)), d)
    

    另一个解决方案,谢谢 @divakar :
    arr = df['row_pat'].values
    b = np.all(rolling_window(arr, N) == pat, axis=1)
    
    m = (rolling_window(arr, len(pat)) == pat).all(1)
    m_ext = np.r_[m,np.zeros(len(arr) - len(m), dtype=bool)]
    df['rm1'] = binary_dilation(m_ext, structure=[1]*N, origin=-(N//2))
    

    时间 :
    np.random.seed(456) 
    
    import pandas as pd
    from numpy.random import choice, randn
    from scipy.ndimage.morphology import binary_dilation
    import string
    
    # df constructor
    n_rows = 100000
    df = pd.DataFrame({'date_time': pd.date_range('2/9/2018', periods=n_rows, freq='H'),
                       'group_var': choice(list(string.ascii_uppercase), n_rows),
                       'row_pat': choice([0, 1, 2, 3], n_rows),
                       'values': randn(n_rows)})
    
    # sorting 
    df.sort_values(by=['group_var', 'date_time'], inplace=True)
    
    def rolling_window(a, window):
        shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
        strides = a.strides + (a.strides[-1],)
        c = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
        return c
    
    
    arr = df['row_pat'].values
    b = np.all(rolling_window(arr, N) == pat, axis=1)
    
    m = (rolling_window(arr, len(pat)) == pat).all(1)
    m_ext = np.r_[m,np.zeros(len(arr) - len(m), dtype=bool)]
    df['rm1'] = binary_dilation(m_ext, structure=[1]*N, origin=-(N//2))
    
    arr = df['row_pat'].values
    b = np.all(rolling_window(arr, N) == pat, axis=1)
    c = np.mgrid[0:len(b)][b]
    
    d = [i  for x in c for i in range(x, x+N)]
    df['rm2'] = np.in1d(np.arange(len(arr)), d)
    
    print (df.iloc[460:480])
    
                    date_time group_var  row_pat    values    rm0    rm1    rm2
    12045 2019-06-25 21:00:00         A        3 -0.081152  False  False  False
    12094 2019-06-27 22:00:00         A        1 -0.818167  False  False  False
    12125 2019-06-29 05:00:00         A        0 -0.051088  False  False  False
    12143 2019-06-29 23:00:00         A        0 -0.937589  False  False  False
    12145 2019-06-30 01:00:00         A        3  0.298460  False  False  False
    12158 2019-06-30 14:00:00         A        1  0.647161  False  False  False
    12164 2019-06-30 20:00:00         A        3 -0.735538  False  False  False
    12210 2019-07-02 18:00:00         A        1 -0.881740  False  False  False
    12341 2019-07-08 05:00:00         A        3  0.525652  False  False  False
    12343 2019-07-08 07:00:00         A        1  0.311598  False  False  False
    12358 2019-07-08 22:00:00         A        1 -0.710150   True   True   True
    12360 2019-07-09 00:00:00         A        2 -0.752216   True   True   True
    12400 2019-07-10 16:00:00         A        2 -0.205122   True   True   True
    12404 2019-07-10 20:00:00         A        0  1.342591   True   True   True
    12413 2019-07-11 05:00:00         A        1  1.707748  False  False  False
    12506 2019-07-15 02:00:00         A        2  0.319227  False  False  False
    12527 2019-07-15 23:00:00         A        3  2.130917  False  False  False
    12600 2019-07-19 00:00:00         A        1 -1.314070  False  False  False
    12604 2019-07-19 04:00:00         A        0  0.869059  False  False  False
    12613 2019-07-19 13:00:00         A        2  1.342101  False  False  False
    
    In [225]: %%timeit
         ...: df['rm0'] = (df['row_pat'].rolling(window=N , min_periods=N)
         ...:                           .apply(lambda x: (x==pat).all())
         ...:                           .mask(lambda x: x == 0) 
         ...:                           .bfill(limit=N-1)
         ...:                           .fillna(0)
         ...:                           .astype(bool)
         ...:              )
         ...: 
    1 loop, best of 3: 356 ms per loop
    
    In [226]: %%timeit
         ...: arr = df['row_pat'].values
         ...: b = np.all(rolling_window(arr, N) == pat, axis=1)
         ...: c = np.mgrid[0:len(b)][b]
         ...: d = [i  for x in c for i in range(x, x+N)]
         ...: df['rm2'] = np.in1d(np.arange(len(arr)), d)
         ...: 
    100 loops, best of 3: 7.63 ms per loop
    
    In [227]: %%timeit
         ...: arr = df['row_pat'].values
         ...: b = np.all(rolling_window(arr, N) == pat, axis=1)
         ...: 
         ...: m = (rolling_window(arr, len(pat)) == pat).all(1)
         ...: m_ext = np.r_[m,np.zeros(len(arr) - len(m), dtype=bool)]
         ...: df['rm1'] = binary_dilation(m_ext, structure=[1]*N, origin=-(N//2))
         ...: 
    100 loops, best of 3: 7.25 ms per loop
    

    关于Pandas - 查找和索引与行序列模式匹配的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48710783/

    相关文章:

    python - 查找每个业务季度 pandas 的最大列数

    python - 具有多个输入的网络抓取并收集所需的总 margin

    python - Pandas 数据框列到分层数据结构?

    indexing - 检查字符串是否旋转时超出范围

    sql - 如何在 PostgreSQL 中存储和查询同一文档的版本?

    jquery tablesorter 插件 - 保留替代行颜色

    python - 如何在 Python 中向 DF 添加/估算额外的行

    sql - 优化我的 SQL 查询——选择正确的索引

    javascript - 子节点无法在 Internet Explorer 中工作?

    php - 错误 : ( ! ) 解析错误 : syntax error, 意外 '' (T_ENCAPSED_AND_WHITESPACE),需要标识符 (T_STRING)