关于如何实际使用Python的itertools.groupby()函数,我还没有找到一个可以理解的解释。我想做的是:

取一个列表——在本例中是一个对象化lxml元素的子元素 根据某些标准将其分成几组 然后分别遍历这些组。

我已经查看了文档,但我很难将它们应用到简单的数字列表之外。

那么,如何使用itertools.groupby()呢?还有其他我应该使用的技巧吗?提供良好的“先决条件”阅读的指针也将受到赞赏。


当前回答

我想再举一个例子,说明没有排序的groupby是行不通的。改编自James Sulak的例子

from itertools import groupby

things = [("vehicle", "bear"), ("animal", "duck"), ("animal", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]

for key, group in groupby(things, lambda x: x[0]):
    for thing in group:
        print "A %s is a %s." % (thing[1], key)
    print " "

输出是

A bear is a vehicle.

A duck is a animal.
A cactus is a animal.

A speed boat is a vehicle.
A school bus is a vehicle.

有两组有车辆,而我们只能期待一组

其他回答

Python文档中的示例非常简单:

groups = []
uniquekeys = []
for k, g in groupby(data, keyfunc):
    groups.append(list(g))      # Store group iterator as a list
    uniquekeys.append(k)

在你的例子中,data是一个节点列表,keyfunc是criteria函数的逻辑所在,然后groupby()对数据进行分组。

在调用groupby之前,必须小心地按照条件对数据进行排序,否则它将不起作用。Groupby方法实际上只是遍历一个列表,每当键改变时,它就创建一个新组。

使用itertools的关键是要认识到。Groupby是指只有在可迭代对象中是顺序的项才会被分组在一起。这就是排序工作的原因,因为基本上你在重新排列集合,以便所有满足callback(item)的项现在都按顺序出现在排序的集合中。

也就是说,您不需要对列表进行排序,只需要一个键-值对的集合,其中的值可以根据groupby生成的每个group iterable增长。例如,列表字典。

>>> things = [("vehicle", "bear"), ("animal", "duck"), ("animal", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]
>>> coll = {}
>>> for k, g in itertools.groupby(things, lambda x: x[0]):
...     coll.setdefault(k, []).extend(i for _, i in g)
...
{'vehicle': ['bear', 'speed boat', 'school bus'], 'animal': ['duck', 'cactus']}

警告:

语法列表(groupby(…))不会按您想要的方式工作。它似乎破坏了内部迭代器对象,所以使用

for x in list(groupby(range(10))):
    print(list(x[1]))

会产生:

[]
[]
[]
[]
[]
[]
[]
[]
[]
[9]

而不是list(groupby(…)),尝试[(k, list(g)) for k,g in groupby(…)],或者如果你经常使用这种语法,

def groupbylist(*args, **kwargs):
    return [(k, list(g)) for k, g in groupby(*args, **kwargs)]

并且可以访问groupby功能,同时避免那些讨厌的(对于小数据)迭代器。

from random import randint
from itertools import groupby

 l = [randint(1, 3) for _ in range(20)]

 d = {}
 for k, g in groupby(l, lambda x: x):
     if not d.get(k, None):
         d[k] = list(g)
     else:
         d[k] = d[k] + list(g)

上面的代码展示了如何使用groupby根据提供的lambda函数/键对列表进行分组。唯一的问题是输出没有合并,这可以使用字典轻松解决。

例子:

l = [2, 1, 2, 3, 1, 3, 2, 1, 3, 3, 1, 3, 2, 3, 1, 2, 1, 3, 2, 3]

应用groupby后,结果将是:

for k, g in groupby(l, lambda x:x):
    print(k, list(g))

2 [2]
1 [1]
2 [2]
3 [3]
1 [1]
3 [3]
2 [2]
1 [1]
3 [3, 3]
1 [1]
3 [3]
2 [2]
3 [3]
1 [1]
2 [2]
1 [1]
3 [3]
2 [2]
3 [3]

一旦字典被使用如下所示的结果可以很容易地迭代:

{2: [2, 2, 2, 2, 2, 2], 1: [1, 1, 1, 1, 1, 1], 3: [3, 3, 3, 3, 3, 3, 3, 3]}

@CaptSolo,我试过你的例子,但没用。

from itertools import groupby 
[(c,len(list(cs))) for c,cs in groupby('Pedro Manoel')]

输出:

[('P', 1), ('e', 1), ('d', 1), ('r', 1), ('o', 1), (' ', 1), ('M', 1), ('a', 1), ('n', 1), ('o', 1), ('e', 1), ('l', 1)]

如你所见,有两个o和两个e,但它们被分成了不同的组。这时我意识到需要对传递给groupby函数的列表进行排序。所以,正确的用法是:

name = list('Pedro Manoel')
name.sort()
[(c,len(list(cs))) for c,cs in groupby(name)]

输出:

[(' ', 1), ('M', 1), ('P', 1), ('a', 1), ('d', 1), ('e', 2), ('l', 1), ('n', 1), ('o', 2), ('r', 1)]

记住,如果列表没有排序,groupby函数将不起作用!