【问题标题】:How to count longest uninterrupted sequence in pandas如何计算熊猫中最长的不间断序列
【发布时间】:2018-02-23 03:06:33
【问题描述】:

假设我有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,但在所有True 中的Series 中,它将打印0

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    选项 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. 但是您可以看到我们关心的组由2s 表示,并且有四个。这是因为该组是由第一个 False 发起的(它变成了 True(~s))。因此,我们用我们开始使用的布尔掩码来掩盖这个累积和。

      (~s).cumsum()[s]
      
      1    1
      3    2
      4    2
      5    2
      dtype: int64
      
    3. 现在我们看到三个2s 弹出,我们只需要使用一种方法来提取它们。我使用了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
    

    【讨论】:

    • 感谢您的精彩解释。
    • 伙计,这太棒了:-)
    • @piRSquared 添加一个python groupby :-) 干杯 :-),向你学习很多东西,谢谢先生!
    • @piRSquared,感谢并学习了使用 (~a).cumsum() 的新技巧
    • @piRSquared,我怎么知道最长的真值序列出现在哪里?
    【解决方案2】:

    我认为这可以工作

    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
    

    【讨论】:

    • 这很干净。
    • @wen,很好的使用 s.index[~s]
    • 也许我需要花更多的时间来学习python标准库。
    • 如果所有元素都是False,它将返回8,所以代码应该是max([len(list(group)) for key, group in groupby(s.tolist()) if key])
    【解决方案3】:

    您可以使用(受@piRSquared 答案启发):

    s.groupby((~s).cumsum()).sum().max()
    Out[513]: 3.0
    

    另一种使用 lambda func 来执行此操作的选项。

    s.to_frame().apply(lambda x: s.loc[x.name:].idxmin() - x.name, axis=1).max()
    Out[429]: 3
    

    【讨论】:

      【解决方案4】:

      编辑:正如 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 查找False 的所有索引

        array([0, 2, 6, 7])
        

      我们知道Trues 位于Falses 之间。因此,我们可以使用 np.diff 查找这些索引之间的差距。

          array([2, 4, 1])
      
      • 最后减 1,因为Trues 位于这些索引之间。

      • 找出最大的差异。

      【讨论】:

      • 嗯不错的解决方案
      • 同意这很好。但是,如果您在数组的开头或结尾有最长的 True 序列,您的差异将无法捕获它。您需要将False 附加到末尾,然后执行此操作。另外,你不需要s == False~s 就可以了。
      • 我会这样做。随意将它添加到您的答案中,因为它是相同的概念,只有当您想要时(-:(np.diff(np.flatnonzero(np.append(True, np.append(~s.values, True)))) - 1).max() 虽然我建议格式更好。
      • @piRSquared 感谢您提供解决方案。我很感激。
      【解决方案5】:

      您的代码实际上非常接近。它通过一个小修复变得完美:

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

      【讨论】:

        【解决方案6】:

        我不太确定如何使用 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-03-04
          • 1970-01-01
          • 2018-06-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多