在 Python 中跟踪重复列表中的值变化

Track value changes in a repetitive list in Python

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

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]

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

可以使用列表理解来完成,使用 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]

而不是具有 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]

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

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

你可以使用 itertools.izip, itertools.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]

使用 itertools.groupby on enumerate(x) 的另一种选择。 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])

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)