【问题标题】:Finding consecutive segments in a pandas data frame在 pandas 数据框中查找连续段
【发布时间】:2013-01-16 12:31:42
【问题描述】:

我有一个 pandas.DataFrame,在连续的时间点进行测量。随着每次测量,被观察的系统在每个时间点都有不同的状态。因此,DataFrame 还包含一个列,其中包含每次测量时系统的状态。状态变化比测量间隔慢得多。因此,指示状态的列可能如下所示(索引:状态):

1:  3
2:  3
3:  3
4:  3
5:  4
6:  4
7:  4
8:  4
9:  1
10: 1
11: 1
12: 1
13: 1

有没有一种简单的方法可以检索每个连续相等状态段的索引。这意味着我想得到这样的东西:

[[1,2,3,4], [5,6,7,8], [9,10,11,12,13]]

结果也可能与普通列表不同。

目前我能想到的唯一解决方案是手动迭代行,找到段变化点并从这些变化点重建索引,但我希望有一个更简单的解决方案。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    单线:

    df.reset_index().groupby('A')['index'].apply(np.array)
    

    代码示例:

    In [1]: import numpy as np
    
    In [2]: from pandas import *
    
    In [3]: df = DataFrame([3]*4+[4]*4+[1]*4, columns=['A'])
    In [4]: df
    Out[4]:
        A
    0   3
    1   3
    2   3
    3   3
    4   4
    5   4
    6   4
    7   4
    8   1
    9   1
    10  1
    11  1
    
    In [5]: df.reset_index().groupby('A')['index'].apply(np.array)
    Out[5]:
    A
    1    [8, 9, 10, 11]
    3      [0, 1, 2, 3]
    4      [4, 5, 6, 7]
    

    您也可以直接从 groupby 对象中获取信息:

    In [1]: grp = df.groupby('A')
    
    In [2]: grp.indices
    Out[2]:
    {1L: array([ 8,  9, 10, 11], dtype=int64),
     3L: array([0, 1, 2, 3], dtype=int64),
     4L: array([4, 5, 6, 7], dtype=int64)}
    
    In [3]: grp.indices[3]
    Out[3]: array([0, 1, 2, 3], dtype=int64)
    

    要解决 DSM 提到的情况,您可以执行以下操作:

    In [1]: df['block'] = (df.A.shift(1) != df.A).astype(int).cumsum()
    
    In [2]: df
    Out[2]:
        A  block
    0   3      1
    1   3      1
    2   3      1
    3   3      1
    4   4      2
    5   4      2
    6   4      2
    7   4      2
    8   1      3
    9   1      3
    10  1      3
    11  1      3
    12  3      4
    13  3      4
    14  3      4
    15  3      4
    

    现在按两列分组并应用 lambda 函数:

    In [77]: df.reset_index().groupby(['A','block'])['index'].apply(np.array)
    Out[77]:
    A  block
    1  3          [8, 9, 10, 11]
    3  1            [0, 1, 2, 3]
       4        [12, 13, 14, 15]
    4  2            [4, 5, 6, 7]
    

    【讨论】:

    • 这假定值不会在不连续的段中重复——例如,DataFrame([3]*4+[4]*4+[1]*4 + [3]*4, columns=['A']) 会将两组 3 放入同一组中。您可以扫描它们以查找中断,但这只是原始问题的另一个版本。不过,也许有一种方法可以让 pandas groupby 在此处表现得更像 itertools.groupby
    • 谢谢,您的第二个解决方案效果很好。我其实有帝斯曼描述的情况。
    • 如果您想按一些偏差进行分组(例如,组包含所有值都在原始集合中相邻值的 +-1 范围内的值),该怎么办?
    • @ryanjdillon 你可以试试df['block'] = (df.A.diff(1).abs() > 1).cumsum()。但这只检查相邻值的差异,所以如果你有DataFrame([3]*4+[4]*4+[5]*1+[6]*3, columns=['A']),它仍然会产生一个块。
    • 值得注意的是,您实际上并不需要“astype(int)”——pandas 恰好可以对布尔值求和。
    【解决方案2】:

    您可以使用 np.diff() 来测试段的开始/结束位置并迭代这些结果。它是一个非常简单的解决方案,因此可能不是最有效的解决方案。

    a = np.array([3,3,3,3,3,4,4,4,4,4,1,1,1,1,4,4,12,12,12])
    
    prev = 0
    splits = np.append(np.where(np.diff(a) != 0)[0],len(a)+1)+1
    
    for split in splits:
        print np.arange(1,a.size+1,1)[prev:split]
        prev = split
    

    结果:

    [1 2 3 4 5]
    [ 6  7  8  9 10]
    [11 12 13 14]
    [15 16]
    [17 18 19]
    

    【讨论】:

    • 谢谢,实际上 Zelazny7 的解决方案更方便,因为我喜欢将段存储在 DataFrame 中,它会自动实现这一点。
    • 谢谢罗格。 np.diff() 是一个很好的建议。
    猜你喜欢
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 2021-09-23
    • 2017-01-31
    • 2019-03-24
    • 1970-01-01
    • 2019-03-19
    • 1970-01-01
    相关资源
    最近更新 更多