【问题标题】:Counting non-overlapping runs of non-zero values by row in a DataFrame在 DataFrame 中按行计算非零值的非重叠运行
【发布时间】:2017-06-14 15:01:16
【问题描述】:

假设我有以下 Pandas DataFrame

id | a1 | a2 | a3 | a4 
1  | 3  | 0  | 10 | 25   
2  | 0  | 0  | 31 | 15  
3  | 20 | 11 | 6  | 5  
4  | 0  | 3  | 1  | 7  

对于n 的各种值,我想要计算每行中n 连续非零值的非重叠运行次数。期望的输出是:

id | a1 | a2 | a3 | a4 | 2s | 3s | 4s
1  | 3  | 0  | 10 | 25 | 1  | 0  | 0
2  | 0  | 0  | 31 | 15 | 1  | 0  | 0
3  | 20 | 11 | 6  | 5  | 2  | 1  | 1
4  | 0  | 3  | 1  | 7  | 1  | 1  | 0

例如在哪里2s 列中的每个值显示该行中长度为 2 的非重叠游程数,3s 列中的每个值显示相应的长度为 3 的游程数,依此类推。

我想知道是否有任何 Pandas 或 Numpy 方法来处理这个问题?

【问题讨论】:

  • 我不明白你所说的“2s 和 3s 对”是什么意思。例如。整个数据框中根本没有 2(除非你算上 id,但我认为这应该是一个索引?)
  • 对不起我的英语!我的意思是如果连续有 2 个(或 3 个或 4 个)非零值。你有什么建议可以清楚地改写我的问题吗?
  • 哦,我明白了。我认为最好的说法就是您在评论中所说的:“连续连续 2 个(或 3 个或 4 个)非零值。”我想您可以将其称为“连续 2 个(或 3 个或 4 个)连续非零值的 run”。 (绝对不是一对。一对只能有两件事。)
  • 我试图修复它 :) 谢谢!
  • 这有帮助。让我再做一次编辑以进一步澄清它。 (我认为任何进一步的更改都比解释更容易演示。)我完成后,如果您不喜欢它,您可以回滚或再次编辑。

标签: python pandas numpy dataframe


【解决方案1】:

一种管理非重叠特征的解决方案。

def count(row,mins):
    runs=(row!=0).astype(uint8).tobytes().decode().split(chr(0))
    lengths=[len(run) for run in runs]
    return np.floor_divide.outer(lengths,mins).sum(0) 

它使用字符串快速操作来查找所有运行,然后使用 // 查找您可以在每个运行中构建多少给定长度的非重叠运行。

df:

    a1  a2  a3  a4
id                
1    3   0  10  25
2    0   0  31  15
3   20  11   6   5
4    0   3   1   7

np.apply_along_axis(count,1,df,[2,3,4]) 返回

array([[1, 0, 0],
       [1, 0, 0],
       [2, 1, 1],
       [1, 1, 0]], dtype=int32)

这是df 的预期结果。

【讨论】:

    【解决方案2】:

    这是使用2D convolution 解决连续任意数量元素的一种方法 -

    from scipy.signal import convolve2d as conv2
    
    n = 6
    v = np.vstack([(conv2(df.values!=0,[[1]*I])==I).sum(1) for I in range(2,n+1)]).T
    df_v = pd.DataFrame(v, columns = [[str(i)+'s' for i in range(2,n+1)]])
    df_out = pd.concat([df, df_v],1)
    

    基本思路

    基本思想是我们可以使用滑动窗口来对每行中存在的非零值求和。假设我们正在查看有多少三个非零连续出现。因此,我们将使用大小为3 的滑动窗口并获得滑动总和。滑动窗口的所有三个元素都以非零形式出现的所有那些地方将产生3 的总和。因此,我们寻找匹配 3 的总和并计算它们。而已!我们循环遍历所有窗口大小以捕获所有2s3s 等。

    这是一个示例运行,用于计算数组上的 3s -

    In [326]: a
    Out[326]: 
    array([[0, 2, 1, 2, 1, 2],
           [2, 2, 2, 0, 0, 0],
           [2, 2, 1, 1, 1, 1],
           [1, 2, 1, 2, 0, 1]])
    
    In [327]: a!=0
    Out[327]: 
    array([[False,  True,  True,  True,  True,  True],
           [ True,  True,  True, False, False, False],
           [ True,  True,  True,  True,  True,  True],
           [ True,  True,  True,  True, False,  True]], dtype=bool)
    
    In [329]: conv2(a!=0,[[1]*3])
    Out[329]: 
    array([[0, 1, 2, 3, 3, 3, 2, 1],
           [1, 2, 3, 2, 1, 0, 0, 0],
           [1, 2, 3, 3, 3, 3, 2, 1],
           [1, 2, 3, 3, 2, 2, 1, 1]])
    
    In [330]: conv2(a!=0,[[1]*3])==3
    Out[330]: 
    array([[False, False, False,  True,  True,  True, False, False],
           [False, False,  True, False, False, False, False, False],
           [False, False,  True,  True,  True,  True, False, False],
           [False, False,  True,  True, False, False, False, False]], dtype=bool)
    
    In [331]: (conv2(a!=0,[[1]*3])==3).sum(1)
    Out[331]: array([3, 1, 4, 2])
    

    示例运行 -

    In [158]: df_out
    Out[158]: 
       a1  a2  a3  a4  a5  a6  2s  3s  4s  5s  6s
    0   1   2   1   0   0   2   2   1   0   0   0
    1   1   1   2   1   0   1   3   2   1   0   0
    2   1   1   0   0   1   1   2   0   0   0   0
    3   2   2   1   0   2   2   3   1   0   0   0
    

    请注意,如果第一列是'id',那么我们需要跳过它。因此,我们需要在建议的解决方案代码中使用df.values[:,1:] 而不是df.values

    【讨论】:

    • 先生,如果有机会,请您解释一下[(conv2(df.values!=0,[[1]*I])==I).sum(1) for I in range(2,n+1)] 部分吗?
    • 先生,如果我只想在数据帧的尾部检查这个怎么办?
    • @renakre 你说的尾巴到底是什么意思?最后一行?
    • 例如,如果我想得到尾部 1 的序列: (1, 2,35,6,3,2,1,1,1) 我想得到 3 .如果最后没有1,那么0。有意义吗?
    • @renakre 这看起来与这里的问题不同。所以,考虑在 Stackoverflow 上发布一个新问题。
    猜你喜欢
    • 1970-01-01
    • 2014-11-21
    • 1970-01-01
    • 2017-05-26
    • 1970-01-01
    • 2018-05-12
    • 2014-07-27
    • 1970-01-01
    • 2019-11-07
    相关资源
    最近更新 更多