【问题标题】:Track value changes in a repetitive list in Python在 Python 中跟踪重复列表中的值变化
【发布时间】:2015-01-30 18:08:03
【问题描述】:

我有一个包含重复值的列表,如下所示:

x = [1, 1, 1, 2, 2, 2, 1, 1, 1]

此列表由模式匹配正则表达式生成(此处未显示)。该列表保证具有重复值(很多很多重复 - 数百,如果不是数千),并且永远不会随机排列,因为这是正则表达式每次匹配的内容。

我想要的是跟踪条目从以前的值更改的列表索引。所以对于上面的列表x,我想得到一个变化跟踪列表[3, 6],表明x[3]x[6]与之前在列表中的条目不同。

我设法做到了,但我想知道是否有更清洁的方法。这是我的代码:

x = [1, 1, 1, 2, 2, 2, 1, 1, 1]

flag = []
for index, item in enumerate(x):
    if index != 0:
        if x[index] != x[index-1]:
            flag.append(index)

print flag

输出[3, 6]

问题:有没有一种更简洁的方法可以用更少的代码行来做我想做的事?

【问题讨论】:

  • 很好看,您可以通过在第二个 if 语句中使用 index-1 来摆脱 lag,并将第二个 if 更改为 !=,这样您就可以删除 else 和将该代码移至 if
  • @JamesKent 这是个好主意。我更新了问题和代码。谢谢。
  • 您已经拥有item,因此您无需再次访问x[index] 即可与x[index-1] 进行比较

标签: python list repeat


【解决方案1】:

这可以使用列表推导来完成,带有 range 函数

>>> x = [1, 1, 1, 2, 2, 2, 3, 3, 3]
>>> [i for i in range(1,len(x)) if x[i]!=x[i-1] ]
[3, 6]
>>> x = [1, 1, 1, 2, 2, 2, 1, 1, 1]
>>> [i for i in range(1,len(x)) if x[i]!=x[i-1] ]
[3, 6]

【讨论】:

    【解决方案2】:

    您可以使用itertools.izipitertools.tee 和列表理解来执行此类操作:

    from itertools import izip, tee
    it1, it2 = tee(x)
    next(it2)
    print [i for i, (a, b) in enumerate(izip(it1, it2), 1) if a != b]
    # [3, 6]
    

    enumerate(x) 上使用itertools.groupby 的另一种选择。 groupby 将相似的项目组合在一起,所以我们只需要除第一个之外的每个组的第一个项目的索引:

    from itertools import groupby
    from operator import itemgetter
    it = (next(g)[0] for k, g in groupby(enumerate(x), itemgetter(1)))
    next(it) # drop the first group
    print list(it)
    # [3, 6]
    

    如果 NumPy 是一个选项:

    >>> import numpy as np
    >>> np.where(np.diff(x) != 0)[0] + 1
    array([3, 6])
    

    【讨论】:

    • 在我意识到之前我在想list(accumulate(len(list(g)) for k,g in groupby(x)))[:-1]..
    【解决方案3】:

    我在这里添加包含列表理解的强制性答案。

    flag = [i+1 for i, value in enumerate(x[1:]) if (x[i] != value)]
    

    【讨论】:

      【解决方案4】:

      而不是具有O(n) 复杂性的多索引,您可以使用迭代器来检查列表中的下一个元素:

      >>> x =[1, 1, 1, 2, 2, 2, 3, 3, 3]
      >>> i_x=iter(x[1:])
      >>> [i for i,j in enumerate(x[:-1],1) if j!=next(i_x)]
      [3, 6]
      

      【讨论】:

      • 这是二次运行时,它不能正确处理[1, 1, 1, 2, 2, 2, 1, 1, 1]的情况。
      • @SvenMarnach +1,出于这个原因,我反对使用set
      【解决方案5】:

      itertools.izip_longest 就是你要找的东西:

      from itertools import islice, izip_longest
      
      flag = []
      leader, trailer = islice(iter(x), 1), iter(x)
      for i, (current, previous) in enumerate(izip_longest(leader, trailer)):
          # Skip comparing the last entry to nothing
          # If None is a valid value use a different sentinel for izip_longest
          if leader is None:
              continue
          if current != previous:
              flag.append(i)
      

      【讨论】:

        猜你喜欢
        • 2022-01-20
        • 1970-01-01
        • 2019-01-17
        • 2021-01-07
        • 2017-05-04
        • 2018-11-07
        • 2011-03-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多