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

时间:2008-08-03 18:27:10

标签: python iteration

我无法找到关于如何实际使用Python的itertools.groupby()函数的可理解的解释。我想要做的是:

  • 列出一个清单 - 在这种情况下,是一个客观化的lxml元素的孩子
  • 根据某些条件将其分组
  • 然后分别迭代这些组中的每一个。

我已经审核了the documentationthe examples,但我在尝试将它们应用到简单的数字列表之外时遇到了麻烦。

那么,我如何使用itertools.groupby()?我应该使用另一种技术吗?指向良好的“先决条件”阅读的指针也将受到赞赏。

14 个答案:

答案 0 :(得分:587)

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


我没有得到的部分是示例构造

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 + "."

这将为您提供输出:

  动物:熊和鸭。
  植物:仙人掌   车辆:快艇和校车。

答案 1 :(得分:70)

你能告诉我们你的代码吗?

Python文档的示例非常简单:

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

因此,在您的情况下,数据是节点列表,keyfunc是条件函数逻辑的位置,然后groupby()对数据进行分组。

在致电groupby之前,您必须小心按标准对数据进行排序,否则它将无效。 groupby方法实际上只是遍历列表,每当密钥更改时,它都会创建一个新组。

答案 2 :(得分:37)

itertools.groupby是一个用于对项目进行分组的工具。

the docs开始,我们会进一步收集它可能会做的事情:

  

# [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B

     

# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D

groupby个对象产生关键组对,其中组是生成器。

功能

  • 甲。将连续项目组合在一起
  • B中。在给定可排序的可迭代的情况下对项的所有匹配项进行分组
  • ℃。指定如何使用键功能对项目进行分组

比较

# Define a printer for comparing outputs
>>> def print_groupby(iterable, key=None):
...    for k, g in it.groupby(iterable, key):
...        print("key: '{}'--> group: {}".format(k, list(g)))

# Feature A: group consecutive occurrences
>>> print_groupby("BCAACACAADBBB")
key: 'B'--> group: ['B']
key: 'C'--> group: ['C']
key: 'A'--> group: ['A', 'A']
key: 'C'--> group: ['C']
key: 'A'--> group: ['A']
key: 'C'--> group: ['C']
key: 'A'--> group: ['A', 'A']
key: 'D'--> group: ['D']
key: 'B'--> group: ['B', 'B', 'B']

# Feature B: group all occurrences
>>> print_groupby(sorted("BCAACACAADBBB"))
key: 'A'--> group: ['A', 'A', 'A', 'A', 'A']
key: 'B'--> group: ['B', 'B', 'B', 'B']
key: 'C'--> group: ['C', 'C', 'C']
key: 'D'--> group: ['D']

# Feature C: group by a key function
>>> key = lambda x: x.islower()
>>> print_groupby(sorted("bCAaCacAADBbB"), key)
key: 'False'--> group: ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'D']
key: 'True'--> group: ['a', 'a', 'b', 'b', 'c']

用途

注意:后面几个例子来自VíctorTerrón的PyCon (talk) (Spanish),“黎明与Itertools的功夫”。另请参阅用C。

编写的groupby source code

响应

# OP: Yes, you can use `groupby`, e.g. 
[do_something(list(g)) for _, g in groupby(lxml_elements, key=criteria_func)]

答案 3 :(得分:37)

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

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

将为您提供一个2元组列表,其中第一个元素是char,第二个元素是重复次数。

编辑:请注意,这是将itertools.groupby与SQL GROUP BY语义分开的原因:itertools不会(通常不能)提前对迭代器进行排序,因此具有相同的组“密钥“未合并。

答案 4 :(得分:25)

另一个例子:

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中的值进行分组。在这种情况下,首先输出一组零,然后输出一组1,然后再输出一组零。

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]

答案 5 :(得分:19)

警告:

语法列表(groupby(...))将无法按您的意图运行。它似乎破坏了内部迭代器对象,所以使用

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

将产生:

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

相反,列表(groupby(...)),尝试[(k,list(g))为k,g in groupby(...)],或者如果经常使用该语法,

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

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

答案 6 :(得分:9)

我想举一个例子,其中没有排序的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.

有两个车辆组,而一个人只能期待一组

答案 7 :(得分:7)

@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)]

正如你所看到的,有两个和两个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函数将无效

答案 8 :(得分:5)

  

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

您可以使用groupby将事物分组以进行迭代。你给groupby一个可迭代的,一个可选的 key 函数/可调用来检查它们从iterable中出来的项目,并返回一个迭代器,给出一个二元组的结果key callable和另一个iterable中的实际项目。来自帮助:

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

这里有一个groupby使用协程按计数分组的例子,它使用一个可调用的密钥(在这种情况下,coroutine.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])]

答案 9 :(得分:5)

  

排序和分组

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'}

答案 10 :(得分:3)

此基本实现帮助我了解了此功能。希望它对其他人也有帮助:

arr = [(1, "A"), (1, "B"), (1, "C"), (2, "D"), (2, "E"), (3, "F")]

for k,g in groupby(arr, lambda x: x[0]):
    print("--", k, "--")
    for tup in g:
        print(tup[1])  # tup[0] == k
-- 1 --
A
B
C
-- 2 --
D
E
-- 3 --
F

答案 11 :(得分:1)

我遇到的一个有用的例子可能会有所帮助:

from itertools import groupby

#user input

myinput = input()

#creating empty list to store output

myoutput = []

for k,g in groupby(myinput):

    myoutput.append((len(list(g)),int(k)))

print(*myoutput)

样本输入:14445221

样品输出:(1,1)(3,4)(1,5)(2,2)(1,1)

答案 12 :(得分:0)

您可以编写自己的groupby函数:

           def groupby(data):
                kv = {}
                for k,v in data:
                    if k not in kv:
                         kv[k]=[v]
                    else:
                        kv[k].append(v)
           return kv

     Run on ipython:
       In [10]: data = [('a', 1), ('b',2),('a',2)]

        In [11]: groupby(data)
        Out[11]: {'a': [1, 2], 'b': [2]}

答案 13 :(得分:0)

遗憾的是,我认为不建议使用 itertools.groupby()。安全使用太难了,只需几行代码就可以写出按预期工作的东西。

def my_group_by(iterable, keyfunc):
    """Because itertools.groupby is tricky to use

    The stdlib method requires sorting in advance, and returns iterators not
    lists, and those iterators get consumed as you try to use them, throwing
    everything off if you try to look at something more than once.
    """
    ret = defaultdict(list)
    for k in iterable:
        ret[keyfunc(k)].append(k)
    return dict(ret)

像这样使用它:

def first_letter(x):
    return x[0]

my_group_by('four score and seven years ago'.split(), first_letter)

得到

{'f': ['four'], 's': ['score', 'seven'], 'a': ['and', 'ago'], 'y': ['years']}