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

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

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

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


当前回答

另一个例子:

for key, igroup in itertools.groupby(xrange(12), lambda x: x // 5):
    print key, list(igroup)

结果

0 [0, 1, 2, 3, 4]
1 [5, 6, 7, 8, 9]
2 [10, 11]

注意,igroup是一个迭代器(文档称之为子迭代器)。

这对于分块生成器很有用:

def chunker(items, chunk_size):
    '''Group items in chunks of chunk_size'''
    for _key, group in itertools.groupby(enumerate(items), lambda x: x[0] // chunk_size):
        yield (g[1] for g in group)

with open('file.txt') as fobj:
    for chunk in chunker(fobj):
        process(chunk)

groupby的另一个例子-当键没有排序时。在以下示例中,xx中的项按yy中的值进行分组。在这种情况下,首先输出一组0,然后是一组1,然后又是一组0。

xx = range(10)
yy = [0, 0, 0, 1, 1, 1, 0, 0, 0, 0]
for group in itertools.groupby(iter(xx), lambda x: yy[x]):
    print group[0], list(group[1])

生产:

0 [0, 1, 2]
1 [3, 4, 5]
0 [6, 7, 8, 9]

其他回答

@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函数将不起作用!

我如何使用Python的itertools.groupby()?

您可以使用groupby来对迭代进行分组。你给groupby一个可迭代对象,和一个可选的键函数/可调用对象,用来检查从可迭代对象中取出的项,它返回一个迭代器,给出一个由可调用键的结果和另一个可迭代对象中的实际项组成的二元组。来自帮助:

groupby(iterable[, keyfunc]) -> create an iterator which returns
(key, sub-iterator) grouped by each value of key(value).

下面是groupby使用协程按计数分组的例子,它使用一个键可调用对象(在本例中是corroutine .send)来输出迭代次数的计数和元素的分组子迭代器:

import itertools


def grouper(iterable, n):
    def coroutine(n):
        yield # queue up coroutine
        for i in itertools.count():
            for j in range(n):
                yield i
    groups = coroutine(n)
    next(groups) # queue up coroutine

    for c, objs in itertools.groupby(iterable, groups.send):
        yield c, list(objs)
    # or instead of materializing a list of objs, just:
    # return itertools.groupby(iterable, groups.send)

list(grouper(range(10), 3))

打印

[(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]

重要提示:您必须首先对数据进行排序。


我没有理解的部分是在例子结构中

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

K是当前分组键,g是一个迭代器,可用于遍历由该分组键定义的组。换句话说,groupby迭代器本身返回迭代器。

下面是一个例子,使用了更清晰的变量名:

from itertools import groupby

things = [("animal", "bear"), ("animal", "duck"), ("plant", "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("")
    

这将给你输出:

熊是动物。 鸭子是一种动物。 仙人掌是一种植物。 快艇是交通工具。 校车是一种交通工具。

在这个例子中,things是一个元组列表,每个元组中的第一项是第二项所属的组。

groupby()函数有两个参数:(1)要分组的数据和(2)要分组的函数。

这里,lambda x: x[0]告诉groupby()使用每个元组中的第一项作为分组键。

在上面的for语句中,groupby返回三个(键,组迭代器)对——每个唯一键一次。您可以使用返回的迭代器遍历该组中的每一项。

下面是一个略有不同的例子,使用相同的数据,使用列表理解:

for key, group in groupby(things, lambda x: x[0]):
    listOfThings = " and ".join([thing[1] for thing in group])
    print(key + "s:  " + listOfThings + ".")

这将给你输出:

动物:熊和鸭。 植物:仙人掌。 交通工具:快艇、校车。

groupby的一个新技巧是在一行中运行长度编码:

[(c,len(list(cgen))) for c,cgen in groupby(some_string)]

会给你一个二元组列表,其中第一个元素是char,第二个元素是重复的次数。

编辑:注意这是itertools的区别。来自SQL GROUP BY语义的groupby: itertools不会(通常也不能)提前对迭代器排序,因此具有相同“key”的组不会合并。

排序和分组

from itertools import groupby

val = [{'name': 'satyajit', 'address': 'btm', 'pin': 560076}, 
       {'name': 'Mukul', 'address': 'Silk board', 'pin': 560078},
       {'name': 'Preetam', 'address': 'btm', 'pin': 560076}]


for pin, list_data in groupby(sorted(val, key=lambda k: k['pin']),lambda x: x['pin']):
...     print pin
...     for rec in list_data:
...             print rec
... 
o/p:

560076
{'name': 'satyajit', 'pin': 560076, 'address': 'btm'}
{'name': 'Preetam', 'pin': 560076, 'address': 'btm'}
560078
{'name': 'Mukul', 'pin': 560078, 'address': 'Silk board'}