为什么itertools groupby没有按预期工作?

时间:2018-01-30 17:46:39

标签: python python-3.x itertools

我试图将整数列表分成奇数和偶数组。

>>> from itertools import groupby
>>> L = [1,2,3,4]
>>> grouped = [list(g) for k,g in groupby(L, key=lambda x: x % 2)]
>>> grouped
[[1], [2], [3], [4]]

显然,不是我想要的。但是,如果我首先使用相同的lambda键对L进行排序,它将按预期工作:

>>> L.sort(key=lambda x: x % 2)
>>> L
[2, 4, 1, 3]
>>> grouped = [list(g) for k,g in groupby(L, key=lambda x: x % 2)]
>>> grouped
[[2, 4], [1, 3]]

我不明白为什么要预先分拣。似乎groupby应该遍历列表中的整数,根据键函数为每个值赋值,然后对它们进行分组 - 无论列表顺序如何。

2 个答案:

答案 0 :(得分:2)

itertools.groupby会将符合关键功能的连续项目分组。请参阅下面的(键,组)对。请注意最后相邻的偶数项会发生什么:

>>> from itertools import groupby


>>> lst = [1, 2, 3, 4, 6, 8]
>>> grouped = [(k, list(g)) for k, g in groupby(lst, key=lambda x: x % 2)]
>>> grouped
[(1, [1]), (0, [2]), (1, [3]), (0, [4, 6, 8])]

以下是一些获得平均和赔率的方法:

>>> lst = [1, 2, 3, 4]

# OK, post-process groupby iterator
>>> grouped = [(k, list(g)) for k, g in groupby(lst, key=lambda x: x % 2)]
>>> evens = [x[1][0] for x in grouped if not x[0]]
>>> odds = [x[1][0] for x in grouped if x[0]]
>>> evens, odds
([2, 4], [1, 3])

# Better, pre-"sort"/rearrange iterable then groupby (see comments)
>>> key = lambda x: x % 2
>>> rearranged = sorted(lst, key=key)
>>> evens, odds = [(list(g)) for k, g in groupby(rearranged, key=key)]
>>> evens, odds
([2, 4], [1, 3])

# Even Better, simple list comprehensions
>>> evens, odds = [x for x in lst if not x % 2], [x for x in lst if x % 2]
>>> evens, odds
([2, 4], [1, 3])

有关itertools.groupby的更多信息,请参阅docs和此post

替代

对于复杂分组,您可以映射函数并在defaultdict中收集值。

>>> import collections as ct


>>> def even_odd(elem):
...     key = "odd" if elem % 2 else "even"
...     return key, elem

>>> dd = ct.defaultdict(list)
>>> for k, v in map(even_odd, range(1, 5)):
...     dd[k].append(v)  
>>> dd
defaultdict(list, {'even': [2, 4], 'odd': [1, 3]})

答案 1 :(得分:1)

知道了。我以前读过文档并认为我理解,但显然我没有。这是重点:

“每次键函数的值发生变化时,它都会生成一个中断或新组(这就是为什么通常需要使用相同的键函数对数据进行排序的原因)。” [强调我的。]

在发布我的问题之前,我应该重新阅读这篇文章。遗憾。