如何在pandas中计算最长的连续序列

16

假设我有如下的pd.Series

s = pd.Series([False, True, False,True,True,True,False, False])    

0    False
1     True
2    False
3     True
4     True
5     True
6    False
7    False
dtype: bool

我想知道最长的True序列长度是多少,在这个例子中,它是3。

我用了一个愚蠢的方法。

s_list = s.tolist()
count = 0
max_count = 0
for item in s_list:
    if item:
        count +=1
    else:
        if count>max_count:
            max_count = count
        count = 0
print(max_count)

它将打印3,但在全是TrueSeries中,它将打印0

6个回答

28

选项 1
使用该系列本身来掩盖否定的累积总和。 然后使用value_counts

(~s).cumsum()[s].value_counts().max()

3

解释

  1. (~s).cumsum() 是一种生成不同的 True/False 组的标准方法。

0    1
1    1
2    2
3    2
4    2
5    2
6    3
7    4
dtype: int64
  • 但是您可以看到,我们关心的组由2表示,而且有四个。这是因为该组是由第一个False(使用(~s)变为True)发起的。因此,我们使用我们最初的布尔掩码来屏蔽这个累加和。

  • (~s).cumsum()[s]
    
    1    1
    3    2
    4    2
    5    2
    dtype: int64
    
  • 现在我们看到出现了三个2,我们只需要使用一种方法来提取它们。我使用了value_countsmax


  • 选项2
    使用factorizebincount

    a = s.values
    b = pd.factorize((~a).cumsum())[0]
    np.bincount(b[a]).max()
    
    3
    

    解释
    这是与选项1类似的解释。 主要区别在于我如何找到最大值。我使用pd.factorize将值标记为从0到唯一值总数的整数。 鉴于我们在(~a).cumsum()中拥有的实际值,我们并不严格需要这部分。 我使用它是因为它是通用工具,可用于任意组名。

    pd.factorize之后,我在np.bincount中使用这些整数值,该函数累积每个整数使用的总次数。 然后取最大值。


    选项3
    如选项2的解释所述,这也有效:

    a = s.values
    np.bincount((~a).cumsum()[a]).max()
    
    3
    

    1
    感谢您的精彩解释。 - Dawei
    真棒,这太好了 :-) - BENY
    @piRSquared 加入一个Python的groupby :-) 干杯 :-),从您那里学到了很多,非常感谢您! - BENY
    @piRSquared,谢谢并学到了一个新技巧,使用(〜a)。cumsum() - Allen Qin
    3
    @piRSquared,我如何知道最长的True序列出现在哪里? - Bernardo Trindade
    这段代码如何修改才能找到最长的连续序列,比如特定整数值? - cjstevens

    5

    我认为这可能可行。

    pd.Series(s.index[~s].values).diff().max()-1
    Out[57]: 3.0
    

    除了pandas之外,我们还可以回到Python的groupby。

    from itertools import groupby
    max([len(list(group)) for key, group in groupby(s.tolist())])
    Out[73]: 3
    

    更新:

    from itertools import compress
    max(list(compress([len(list(group)) for key, group in groupby(s.tolist())],[key for key, group in groupby(s.tolist())])))
    Out[84]: 3
    

    这非常干净。 - Tai
    @wen,很好地运用了s.index[~s]。 - Allen Qin
    也许我需要花更多的时间学习Python标准库。 - Dawei
    如果所有元素都是“False”,它将返回“8”,因此代码应为max([len(list(group)) for key, group in groupby(s.tolist()) if key]) - Dawei

    2

    编辑:正如piRSquared所提到的,我的先前解决方案需要在系列开头和结尾附加两个False。piRSquared基于此提供了一个答案。

    (np.diff(np.flatnonzero(np.append(True, np.append(~s.values, True)))) - 1).max()
    

    我的原始测试如下:

    (np.diff(s.where(~s).dropna().index.values) - 1).max()
    

    (如果最长的True在开头或结尾,这将无法给出正确答案,正如piRSquared所指出的那样。请使用上面由piRSquared提供的解决方案。此工作仅用于说明。)

    说明:

    此方法找到False部分的索引,通过查找False索引之间的间隙,我们可以知道最长的True

    • s.where(s == False).dropna().index.values finds all the indices of False

      array([0, 2, 6, 7])
      
    我们知道True存在于False之间。因此,我们可以使用np.diff来找到这些索引之间的间隔。
        array([2, 4, 1])
    
    • 在结尾减去1,因为True位于这些索引之间。

    • 找到差值的最大值。


    1
    嗯,不错的解决方案。 - BENY
    1
    同意这很好。然而,如果你在数组的开头或结尾有最长的“True”序列,你的diff就会无法捕捉它。你需要在末尾加上“False”,然后再进行操作。另外,你不需要s == False~s就可以了。 - piRSquared
    1
    这是我会做的方式。如果你愿意,可以将其添加到你的答案中,因为它是相同的概念,只是你想要的话 (-: (np.diff(np.flatnonzero(np.append(True, np.append(~s.values, True)))) - 1).max()。虽然我建议格式更好一些。 - piRSquared
    1
    @piRSquared,感谢您提供了解决方案。我非常感激。 - Tai

    2
    您可以使用以下方法(灵感来自@piRSquared的答案):
    s.groupby((~s).cumsum()).sum().max()
    Out[513]: 3.0
    

    另一种方法是使用lambda函数来完成此操作。
    s.to_frame().apply(lambda x: s.loc[x.name:].idxmin() - x.name, axis=1).max()
    Out[429]: 3
    

    2

    您的代码实际上非常接近完美,只需要进行一些小的修正:

    count = 0
    maxCount = 0
    for item in s:
        if item:
            count += 1
            if count > maxCount:
                maxCount = count
        else:
            count = 0
    print(maxCount)
    

    1

    我不确定如何在pandas中实现,但使用itertools.groupby怎么样?

    >>> import pandas as pd
    >>> s = pd.Series([False, True, False,True,True,True,False, False])
    >>> max(sum(1 for _ in g) for k, g in groupby(s) if k)
    3
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接