代码之家  ›  专栏  ›  技术社区  ›  Maranda Ridgway

如何找到多个列的最大连续数?

  •  4
  • Maranda Ridgway  · 技术社区  · 8 年前

    我需要确定满足多个列的特定标准的最大连续值数。

    如果我的df是:

    A    B    C    D    E
    26   24   21   23   24
    26   23   22   15   23 
    24   19   17   11   15     
    27   22   28   24   24 
    26   27   30   23   11 
    26   26   29   27   29
    

    我想知道每列出现超过25个数字的最大连续次数。因此,输出为:

    A 3
    B 2
    C 3
    D 1
    E 1
    

    使用以下代码,我可以一次获得一列的结果;有没有办法像上面那样创建一个表,而不是为每一列重复(我总共有40多列)。

    df.A.isnull().astype(int).groupby(df.A.notnull().astype(int).cumsum()).sum().max()
    

    提前谢谢。

    4 回复  |  直到 8 年前
        1
  •  3
  •   BENY    8 年前

    这是你想要的吗? pandas 方法(PS:从没想过我能做到一行LOL)

    (df>25).apply(lambda x :x.groupby(x.diff().ne(0).cumsum()).cumcount()+1).mask(df<25).max()
    Out[320]: 
    A    3.0
    B    2.0
    C    3.0
    D    1.0
    E    1.0
    dtype: float64
    
        2
  •  1
  •   akuiper    8 年前

    一个选项使用 numpy 要计算最大连续:

    def max_consecutive(arr):
        # calculate the indices where the condition changes
        split_indices = np.flatnonzero(np.ediff1d(arr.values, to_begin=1, to_end=1))
    
        # calculate the chunk length of consecutive values and pick every other value based on 
        # the initial value
        try:
            max_size = np.diff(split_indices)[not arr.iat[0]::2].max()
        except ValueError:
            max_size = 0
        return max_size
    
    df.gt(25).apply(max_consecutive)
    #A    3
    #B    2
    #C    3
    #D    1
    #E    1
    #dtype: int64
    

    计时 与其他方法相比:

    %timeit df.gt(25).apply(max_consecutive)
    # 520 µs ± 6.92 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %timeit (df>25).apply(lambda x :x.groupby(x.diff().ne(0).cumsum()).cumcount()+1).mask(df<25).max(0)
    # 10.3 ms ± 221 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
        3
  •  1
  •   Divakar    8 年前

    这是NumPy的-

    # mask is 2D boolean array representing islands as True values per col
    def max_island_len_cols(mask):
        m,n = mask.shape
        out = np.zeros(n,dtype=int)
        b = np.zeros((m+2,n),dtype=bool)
        b[1:-1] = mask
        for i in range(mask.shape[1]):
            idx = np.flatnonzero(b[1:,i] != b[:-1,i])
            if len(idx)>0:
                out[i] = (idx[1::2] - idx[::2]).max()
        return out
    
    output = pd.Series(max_island_len_cols(df.values>25), index=df.columns)
    

    样本运行-

    In [690]: df
    Out[690]: 
        A   B   C   D   E
    0  26  24  21  23  24
    1  26  23  22  15  23
    2  24  19  17  11  15
    3  27  22  28  24  24
    4  26  27  30  23  11
    5  26  26  29  27  29
    
    In [690]: 
    
    In [691]: pd.Series(max_island_len_cols(df.values>25), index=df.columns)
    Out[691]: 
    A    3
    B    2
    C    3
    D    1
    E    1
    dtype: int64
    

    运行时测试

    受给定样本的启发,其数字在范围内 (24,28) 40 cols,让我们设置一个更大的输入数据框,并测试所有的解决方案-

    # Input dataframe
    In [692]: df = pd.DataFrame(np.random.randint(24,28,(1000,40)))
    
    # Proposed in this post
    In [693]: %timeit pd.Series(max_island_len_cols(df.values>25), index=df.columns)
    1000 loops, best of 3: 539 µs per loop
    
    # @Psidom's solution
    In [694]: %timeit df.gt(25).apply(max_consecutive)
    1000 loops, best of 3: 1.81 ms per loop
    
    # @Wen's solution
    In [695]: %timeit (df>25).apply(lambda x :x.groupby(x.diff().ne(0).cumsum()).cumcount()+1).mask(df<25).max(0)
    10 loops, best of 3: 95.2 ms per loop
    
        4
  •  0
  •   FabienP    8 年前

    一种使用 pandas scipy.ndimage.label

    import pandas as pd
    from scipy.ndimage import label
    
    struct = [[0, 1, 0],     # Structure used for segmentation
              [0, 1, 0],     # Equivalent to axis=0 in `numpy`
              [0, 1, 0]]     # Or 'columns' in `pandas`
    
    labels, nlabels = label(df > 25, structure=struct)
    
    >>> labels               # Labels for each column-wise block of consecutive numbers > 25
    Out[]:
    array([[1, 0, 0, 0, 0],
           [1, 0, 0, 0, 0],
           [0, 0, 0, 0, 0],
           [2, 0, 3, 0, 0],
           [2, 4, 3, 0, 0],
           [2, 4, 3, 5, 6]])
    
    labels_df = pd.DataFrame(columns=df.columns, data=labels)  # Add original columns names
    
    res = (labels_df.apply(lambda x: x.value_counts())  # Execute `value_counts` on each column
                    .iloc[1:]                           # slice results for labels > 0
                    .max())                             # and get max value
    
    >>> res
    Out[]:
    A    3.0
    B    2.0
    C    3.0
    D    1.0
    E    1.0
    dtype: float64