如何在可迭代对象元素中使用itertools.groupby的键值?

9 投票
3 回答
10796 浏览
提问于 2025-04-16 02:32

为了说明这个问题,我先从一个包含二元组的列表开始:

import itertools
import operator

raw = [(1, "one"),
       (2, "two"),
       (1, "one"),
       (3, "three"),
       (2, "two")]

for key, grp in itertools.groupby(raw, key=lambda item: item[0]):
    print key, list(grp).pop()[1]

得到的结果是:

1 one
2 two
1 one
3 three
2 two

我想调查一下为什么会这样:

for key, grp in itertools.groupby(raw, key=lambda item: item[0]):
    print key, list(grp)

# ---- OUTPUT ----
1 [(1, 'one')]
2 [(2, 'two')]
1 [(1, 'one')]
3 [(3, 'three')]
2 [(2, 'two')]

即使这样做也会给我相同的输出:

for key, grp in itertools.groupby(raw, key=operator.itemgetter(0)):
    print key, list(grp)

我想得到类似这样的结果:

1 one, one
2 two, two
3 three

我在想,这可能是因为关键字在列表中的元组里,而实际上这个元组是作为一个整体在移动。有没有办法得到我想要的输出?也许 groupby() 并不适合这个任务?

3 个回答

3

来自文档

groupby()的操作方式和Unix中的uniq过滤器很像。每当关键函数的值发生变化时,它就会生成一个新的分组(这就是为什么通常需要先用相同的关键函数对数据进行排序)。这种行为和SQL中的GROUP BY不同,后者会聚合相同的元素,而不管它们的输入顺序。

因为你反正是按字典顺序对元组进行排序的,所以你可以直接调用sorted

for key, grp in itertools.groupby( sorted( raw ), key = operator.itemgetter( 0 ) ):
    print( key, list( map( operator.itemgetter( 1 ), list( grp ) ) ) )
7

我觉得有一个更简单的方法可以得到你想要的结果。

>>> from collections import defaultdict
>>> d=defaultdict(list)
>>> for k,v in raw:
...  d[k].append(v)
... 
>>> for k,v in sorted(d.items()):
...  print k, v
... 
1 ['one', 'one']
2 ['two', 'two']
3 ['three']

构建 d 的时间复杂度是 O(n),而现在 sorted() 只是在唯一的键上进行排序,而不是在整个数据集上。

13

groupby 是一个功能,它可以把一个可迭代的对象中相邻的、具有相同关键字的元素聚集在一起。要得到你想要的结果,首先需要对 raw 进行排序。

for key, grp in itertools.groupby(sorted(raw), key=operator.itemgetter(0)):
    print key, map(operator.itemgetter(1), grp)

# 1 ['one', 'one']
# 2 ['two', 'two']
# 3 ['three']

撰写回答