【问题标题】:Identify if list has consecutive elements that are equal确定列表是否具有相等的连续元素
【发布时间】:2016-12-07 02:45:37
【问题描述】:

我正在尝试确定一个大列表是否具有相同的连续元素。

那么让我们说:

lst = [1, 2, 3, 4, 5, 5, 6]

在这种情况下,我会返回 true,因为有两个连续的元素 lst[4]lst[5],是相同的值。

我知道这可能可以通过某种循环组合来完成,但我想知道是否有更有效的方法来做到这一点?

【问题讨论】:

标签: python list


【解决方案1】:

如果您正在寻找一种有效的方法并且列表是数字的,您可能希望使用numpy 并应用diff(差异)函数:

>>> numpy.diff([1,2,3,4,5,5,6])
array([1, 1, 1, 1, 0, 1])

然后得到一个关于是否有任何连续元素的结果:

>>> numpy.any(~numpy.diff([1,2,3,4,5,5,6]).astype(bool))

这首先执行diff,反转答案,然后检查结果元素中的any 是否非零。

同样,

>>> 0 in numpy.diff([1, 2, 3, 4, 5, 5, 6])

也运行良好,速度与np.any 方法相似(最后一个版本归功于heracho)。

【讨论】:

  • 如果有任何重复,这不会返回True 吗?听起来问题是要求连续重复值。
  • @johnchase:不,因为diff 适用于连续元素(不排序)
  • 好的,我知道了。不错的解决方案!
  • (0 in np.diff([1, 2, 3, 4, 5, 5, 6])) 也会这样做。
  • @heracho - 好点 - 与 in 相比,我曾期望 np.any 提供显着的加速,但事实证明,在使用 %%timeit 进行测试时,它们的速度非常相似ipython!
【解决方案2】:

您可以在any() 中使用itertools.groupby() 和生成器表达式 *:

>>> from itertools import groupby
>>> any(sum(1 for _ in g) > 1 for _, g in groupby(lst))
True

或者作为一种更 Pythonic 的方式,您可以使用 zip(),以检查您的列表中是否至少有两个相等的连续项目:

>>> any(i==j for i,j in zip(lst, lst[1:])) # In python-2.x,in order to avoid creating a 'list' of all pairs instead of an iterator use itertools.izip()
True

注意:当你想检查是否有超过 2 个连续相等的项目时,第一种方法很好,否则,在这种情况下,第二种方法占了上风!


* 使用sum(1 for _ in g) 代替len(list(g)) 在内存使用方面非常优化(不是一次读取内存中的整个列表),但后者稍快。

【讨论】:

  • 第一个解输 1。第二次获得 1 分。 itertools 没有那么清晰。
  • @Holloway 是的,我刚刚提到了第一种方法的优势。
【解决方案3】:

一个简单的for 循环应该可以做到:

def check(lst):
    last = lst[0]
    for num in lst[1:]:
        if num == last:
            return True
        last = num
    return False


lst = [1, 2, 3, 4, 5, 5, 6]
print (check(lst)) #Prints True

在这里,在每个循环中,我检查当前元素是否等于前一个元素。

【讨论】:

    【解决方案4】:

    您可以使用简单 any 条件:

    lst = [1, 2, 3, 4, 5, 5, 6]
    any(lst[i]==lst[i+1] for i in range(len(lst)-1))
    #outputs:
    True
    

    any 返回True 如果任何可迭代元素是True

    【讨论】:

    • 你能解释一下any函数的作用吗?谢谢
    • 这就是我的答案。值得注意的是xrange如果你使用python2更好。
    • @VaibhavBajaj,如果任何参数(或迭代的东西)是True,则返回True
    【解决方案5】:

    如果您想找出 3 个连续值是否等于 7,我的解决方案。例如,intList = (7, 7, 7, 8, 9, 1) 的元组:

    for i in range(len(intList) - 1):
            if intList[i] == 7 and intList[i + 2] == 7 and intList[i + 1] == 7:
                return True
        return False
    

    【讨论】:

      【解决方案6】:

      这里有一个更通用的numpy one-liner:

      number = 7
      n_consecutive = 3
      arr = np.array([3, 3, 6, 5, 8, 7, 7, 7, 4, 5])
      #                              ^  ^  ^ 
       
      np.any(np.convolve(arr == number, v=np.ones(n_consecutive), mode='valid') 
             == n_consecutive)[0]
      

      此方法总是搜索整个数组,而来自 @Kasramvd 的方法在第一次满足条件时结束。因此,哪种方法更快取决于这些连续数字情况的稀疏程度。 如果您对连续数字的位置感兴趣,并且必须查看数组的所有元素,这种方法应该更快(对于更大的数组(或/和更长的序列))。

      idx = np.nonzero(np.convolve(arr==number, v=np.ones(n_consecutive), mode='valid') 
                                   == n_consecutive)
      # idx = i: all(arr[i:i+n_consecutive] == number)
      

      如果您对特定值不感兴趣,但对所有连续数字一般来说,@jmetz 的答案略有不同:

      np.any(np.convolve(np.abs(np.diff(arr)), v=np.ones(n_consecutive-1), mode='valid') == 0)
      #                  ^^^^^^
      # EDIT see djvg's comment
      

      【讨论】:

      • any(convolve(diff(...), ...), ...) 解决方案会产生误报:它基本上计算连续元素之间差异的移动总和,然后检查该总和是否在任何地方为零。但是,nonzero 差异的总和(即连续元素不相等)也可以为零。最小示例:arr=[1,0,1,0,1]n_consecutive=3。如果您查看示例的等效卷积核,问题也会变得很明显:[1,0,-1]
      • 你是对的。我想一个简单的解决方法是获取差异的绝对值。
      【解决方案7】:

      Python 3.10 开始,新的pairwise 函数提供了一种滑过连续元素对的方法,以便我们可以测试连续元素之间的质量:

      from itertools import pairwise
      
      any(x == y for (x, y) in pairwise([1, 2, 3, 4, 5, 5, 6]))
      # True
      

      pairwise的中间结果:

      pairwise([1, 2, 3, 4, 5, 5, 6])
      # [(1, 2), (2, 3), (3, 4), (4, 5), (5, 5), (5, 6)]
      

      【讨论】:

        【解决方案8】:

        scleronomic's answer 中建议的卷积方法非常有前途,特别是如果您要寻找两个以上的连续元素。

        但是,该答案中提出的实现可能不是最有效的,因为它包含两个步骤:diff(),然后是convolve()

        替代实现

        如果我们认为diff()也可以使用卷积计算,我们可以将这两个步骤合并为一个卷积。

        下面的替代实现只需要对完整信号进行一次卷积,如果信号有很多元素,这是有利的。

        请注意,我们不能取diff 的绝对值(为了防止误报,如this comment 中所述),因此我们向单元内核添加了一些随机噪声。

        # example signal
        signal = numpy.array([1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0])
        # minimum number of consecutive elements
        n_consecutive = 3
        # convolution kernel for weighted moving sum (with small random component)
        rng = numpy.random.default_rng()
        random_kernel = 1 + 0.01 * rng.random(n_consecutive - 1)
        # convolution kernel for first-order difference (similar to numpy.diff)
        diff_kernel = [1, -1]
        # combine the kernels so we only need to do one convolution with the signal
        combined_kernel = numpy.convolve(diff_kernel, random_kernel, mode='full')
        # convolve the signal to get the moving weighted sum of differences
        moving_sum_of_diffs = numpy.convolve(signal, combined_kernel, mode='valid')
        # check if moving sum is zero anywhere
        result = numpy.any(moving_sum_of_diffs == 0)
        

        有关卷积的详细讨论,请参阅DSP guide

        时间

        两种实现的区别归结为:

        def original(signal, unit_kernel):
            return numpy.convolve(numpy.abs(numpy.diff(signal)), unit_kernel, mode='valid')
        
        
        def alternative(signal, combined_kernel):
            return numpy.convolve(signal, combined_kernel, mode='valid')
        

        unit_kernel = numpy.ones(n_consecutive - 1)combined_kernel 在上面定义。

        比较这两个函数,使用timeit,表明alternative() 可以快几倍,对于小内核大小(即n_consecutive 的小值)。但是,对于较大的内核大小,优势变得可以忽略不计,因为卷积占主导地位(与 diff 相比)。

        注意事项:

        • 对于较大的内核,我更喜欢原来的两步法,因为我认为它更容易理解。
        • 由于数字问题,可能需要将 numpy.any(moving_sum_of_diffs == 0) 替换为 numpy.any(numpy.abs(moving_sum_of_diffs) < very_small_number),参见例如here

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-03-04
          • 2016-03-13
          • 1970-01-01
          • 1970-01-01
          • 2018-04-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多