如何使用itertools.groupby()获取每个项的索引和出现次数

7 投票
4 回答
4172 浏览
提问于 2025-04-18 02:25

这是我的故事,我有两个列表:

list_one=[1,2,9,9,9,3,4,9,9,9,9,2]
list_two=["A","B","C","D","A","E","F","G","H","Word1","Word2"]

我想找到第一个列表中连续出现的9的索引,这样我就可以从第二个列表中获取对应的字符串。我尝试过:

group_list_one= [(k, sum(1 for i in g),pdn.index(k)) for k,g in groupby(list_one)]

我本来希望能找到每个元组中第一个9的索引,然后再从那里继续,但这并没有成功……

我该怎么做呢?
附注:我看过itertools的文档,但对我来说似乎很模糊……
提前谢谢你!

编辑:
我期望的输出是(键,出现次数,第一个出现的索引),类似于:

[(9, 3, 2), (9, 4, 7)]

4 个回答

1

这个问题看起来太复杂,不适合用列表推导式来处理。

element_index = 0 #the index in list_one of the first element in a group
for element, occurrences in itertools.groupby(list_one):
    count = sum(1 for i in occurrences)
    yield (element, count, element_index)
    element_index += count

如果你想去掉 element_index 这个变量,可以考虑一下一个累加和函数需要做什么,它的值是依赖于之前所有已经计算过的值的。

3

好吧,这可能不是最优雅的解决方案,但我来试试:

g = groupby(enumerate(list_one), lambda x:x[1])
l = [(x[0], list(x[1])) for x in g if x[0] == 9]
[(x[0], len(x[1]), x[1][0][0]) for x in l]

这段代码的结果是

[(9, 3, 2), (9, 4, 7)]
4

好吧,我有一个一行解决方案。虽然看起来有点丑,但请耐心听我讲。

先来看看这个问题。我们有一个列表,想用itertools.groupby来计算总和。groupby会给我们一组键和它们重复的次数。在这个阶段,我们无法计算索引,但我们可以轻松找到每个元素出现的次数。

[(key, len(list(it))) for (key, it) in itertools.groupby(list_one)]

现在,真正的问题是我们想要根据之前的数据来计算索引。在大多数一行的常用函数中,我们只关注当前的状态。然而,有一个函数可以让我们回顾过去,那就是reduce

reduce的作用是遍历迭代器,并用上一个函数的结果和新项来执行一个函数。例如,reduce(lambda x,y: x*y, [2,3,4])会先计算2乘以3等于6,然后再计算6乘以4等于24,最后返回24。此外,你还可以选择一个不同的初始值来代替第一个项。

我们在这里使用它——对于每个项,索引将是上一个索引加上上一个出现的次数。为了得到一个有效的列表,我们将使用[(0,0,0)]作为初始值。(最后我们会把它去掉)。

reduce(lambda lst,item: lst + [(item[0], item[1], lst[-1][1] + lst[-1][-1])], 
       [(key, len(list(it))) for (key, it) in itertools.groupby(list_one)], 
       [(0,0,0)])[1:]

如果我们不想添加初始值,我们可以把到目前为止出现的次数加起来。

reduce(lambda lst,item: lst + [(item[0], item[1], sum(map(lambda i: i[1], lst)))],
       [(key, len(list(it))) for (key, it) in itertools.groupby(list_one)], [])

当然,这样会给我们所有的数字。如果我们只想要9,我们可以把整个过程放在filter里面:

filter(lambda item: item[0] == 9, ... )
8

根据你期望的输出结果,可以试试这个:

from itertools import groupby

list_one=[1,2,9,9,9,3,4,9,9,9,9,2]
list_two=["A","B","C","D","A","E","F","G","H","Word1","Word2"]
data = zip(list_one, list_two)
i = 0
out = []

for key, group in groupby(data, lambda x: x[0]):
        number, word = next(group)
        elems = len(list(group)) + 1
        if number == 9 and elems > 1:
            out.append((key, elems, i))
        i += elems

print out

输出结果:

[(9, 3, 2), (9, 4, 7)]

但是如果你真的想要这样的输出:

[(9, 3, 'C'), (9, 4, 'G')]

那么看看这个代码片段:

from itertools import groupby

list_one=[1,2,9,9,9,3,4,9,9,9,9,2]
list_two=["A","B","C","D","A","E","F","G","H","Word1","Word2"]
data = zip(list_one, list_two)
out = []

for key, group in groupby(data, lambda x: x[0]):
    number, word = next(group)
    elems = len(list(group)) + 1
    if number == 9 and elems > 1:
        out.append((key, elems, word))

print out

撰写回答