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

Jam*_*lak 463 python iteration

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

  • 列出一个列表 - 在这种情况下,是一个客观化lxml元素的孩子
  • 根据某些标准将其划分为不同的组
  • 然后分别迭代这些组中的每一个.

我已经阅读了文档示例,但是我在尝试将它们应用到简单的数字列表之外时遇到了麻烦.

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

Jam*_*lak 613

重要说明:您必须先对数据进行排序.


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

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

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 " "
Run Code Online (Sandbox Code Playgroud)

这将为您提供输出:

熊是动物.
鸭子是动物.

仙人掌是一种植物.

快艇是一种车辆.
校车是一种车辆.

在此示例中,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 + "."
Run Code Online (Sandbox Code Playgroud)

这将为您提供输出:

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

  • +1进行排序-在对数据进行分组之前,我不明白您的意思。 (4认同)
  • @DavidCrook参加聚会很晚,但可能会对某人有所帮助。可能是因为未对数组进行排序,所以在`my_collection = [(“ animal”,的前提下,'groupby(sorted(my_collection,key = lambda x:x [0]),lambda x:x [0])) “ bear”),(“ plant”,“ cactus”),(“ animal”,“ duck”)]],并且您想按“ animal or plant”分组 (4认同)
  • @Julian python docs似乎对大多数东西都很好,但是当涉及到迭代器,生成器和软件时,文档大多使我神秘.Django的文档令人倍感莫名其妙. (3认同)
  • 有没有办法预先指定组然后不需要排序? (2认同)
  • itertools 通常为我点击,但我也有一个“阻止”。我很欣赏你的例子——比文档清晰得多。我认为 itertools 倾向于点击或不点击,如果您碰巧遇到类似的问题,则更容易掌握。在野外还不需要这个。 (2认同)

Seb*_*Seb 72

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

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

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

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

在调用之前,您必须小心按标准对数据进行排序,groupby否则它将无法正常工作.groupby方法实际上只是迭代一个列表,每当密钥更改它创建一个新组.

  • 所以你读了`keyfunc`,就像"是的,我确切地知道那是什么,因为这个文档很简单."?难以置信! (28认同)
  • 我相信大多数人已经知道这个“简单”但无用的示例,因为它没有说明要使用哪种“数据”和“ keyfunc”!但是我想您也不知道,否则您将通过澄清它而不只是复制粘贴来帮助人们。还是你 (3认同)
  • 我会说,虽然只是粘贴到文档中,但已经引用的问题绝不是一个有用的答案,下面的附加声明是一个很好的提醒。数据必须首先按 keyfunc 排序。因此,如果用户有一个类列表,并且她希望按 obj.attr_a 进行分组,则 `grouping_target = Sorted(obj_list, key=lambda o: o.attr_a)` ,然后是 `groups = itertools.groupby(grouping_target, key= lambda o: o.attr_a)`。否则,如前所述,它将无法工作,并且您将看到 groupby 键的重复。 (2认同)

pyl*_*ang 49

itertools.groupby 是一个分组项目的工具.

文档中,我们进一步收集了它可能做的事情:

# [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 对象产生关键组对,其中组是生成器.

特征

  • A.将连续项目组合在一起
  • B.给定一个已排序的可迭代项,对所有项目的出现进行分组
  • C.指定如何使用键功能对项目进行分组

比较

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

# 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
>>> # keyfunc = lambda s: s.islower()                      # equivalent
>>> def keyfunc(s):
...     """Return a True if a string is lowercase, else False."""   
...     return s.islower()
>>> print_groupby(sorted("bCAaCacAADBbB"), keyfunc)
key: 'False'--> group: ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'D']
key: 'True'--> group: ['a', 'a', 'b', 'b', 'c']
Run Code Online (Sandbox Code Playgroud)

用途

注:后面的几个例子来自VíctorTerrón的PyCon (谈话) (西班牙语),"Dawn with Itertools的功夫".另请参阅用C编写的groupby源代码.


响应

# OP: Yes, you can use `groupby`, e.g. 
[do_something(list(g)) for _, g in groupby(lxml_elements, criteria_func)]
Run Code Online (Sandbox Code Playgroud)

  • 从技术上讲,文档可能应该说 `[''.join(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D`。 (2认同)
  • 是的。大多数 itertools 文档字符串都是以这种方式“删节”的。由于所有 itertools 都是迭代器,因此它们必须转换为内置函数(`list()`、`tuple()`)或在循环/理解中使用以显示内容。这些是作者可能为了节省空间而排除的冗余。 (2认同)

nim*_*ish 39

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

[(c,len(list(cgen))) for c,cgen in groupby(some_string)]
Run Code Online (Sandbox Code Playgroud)

将给出一个2元组的列表,其中第一个元素是char,第二个元素是重复的数量.

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


use*_*654 25

另一个例子:

for key, igroup in itertools.groupby(xrange(12), lambda x: x // 5):
    print key, list(igroup)
Run Code Online (Sandbox Code Playgroud)

结果是

0 [0, 1, 2, 3, 4]
1 [5, 6, 7, 8, 9]
2 [10, 11]
Run Code Online (Sandbox Code Playgroud)

请注意,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)
Run Code Online (Sandbox Code Playgroud)

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])
Run Code Online (Sandbox Code Playgroud)

生产:

0 [0, 1, 2]
1 [3, 4, 5]
0 [6, 7, 8, 9]
Run Code Online (Sandbox Code Playgroud)


Rus*_*art 21

警告:

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

for x in list(groupby(range(10))):
    print(list(x[1]))
Run Code Online (Sandbox Code Playgroud)

将产生:

[]
[]
[]
[]
[]
[]
[]
[]
[]
[9]
Run Code Online (Sandbox Code Playgroud)

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

def groupbylist(*args, **kwargs):
    return [(k, list(g)) for k, g in groupby(*args, **kwargs)]
Run Code Online (Sandbox Code Playgroud)

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

  • 许多答案指的是你必须在groupby之前排序以获得预期结果的绊脚石.我刚刚遇到这个答案,这解释了我以前从未见过的奇怪行为.我之前没有见过,因为只是现在才尝试列出(groupby(范围(10))为@singular说.之前我总是使用"推荐"方法"手动"迭代通过groupby对象而不是让list()构造函数"自动"执行它. (3认同)

and*_*otn 11

遗憾的是,我不认为 \xe2\x80\x99s 建议使用itertools.groupby(). 它\xe2\x80\x99s 太难安全使用,而且它\xe2\x80\x99s 只需几行就可以编写按预期工作的东西。

\n
def my_group_by(iterable, keyfunc):\n    """Because itertools.groupby is tricky to use\n\n    The stdlib method requires sorting in advance, and returns iterators not\n    lists, and those iterators get consumed as you try to use them, throwing\n    everything off if you try to look at something more than once.\n    """\n    ret = defaultdict(list)\n    for k in iterable:\n        ret[keyfunc(k)].append(k)\n    return dict(ret)\n
Run Code Online (Sandbox Code Playgroud)\n

像这样使用它:

\n
def first_letter(x):\n    return x[0]\n\nmy_group_by('four score and seven years ago'.split(), first_letter)\n
Run Code Online (Sandbox Code Playgroud)\n

要得到

\n
{'f': ['four'], 's': ['score', 'seven'], 'a': ['and', 'ago'], 'y': ['years']}\n
Run Code Online (Sandbox Code Playgroud)\n


kir*_*off 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 " "
Run Code Online (Sandbox Code Playgroud)

输出是

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.
Run Code Online (Sandbox Code Playgroud)

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

  • 您必须首先对数据进行排序,使用您要分组的功能作为键.这在上面的两篇文章中提到,但没有突出显示. (4认同)

Tia*_*ago 8

这个基本实现帮助我理解了这个函数。希望它也能帮助其他人:

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
Run Code Online (Sandbox Code Playgroud)
-- 1 --
A
B
C
-- 2 --
D
E
-- 3 --
F
Run Code Online (Sandbox Code Playgroud)


ped*_*oel 7

@CaptSolo,我试过你的例子,但它没有用.

from itertools import groupby 
[(c,len(list(cs))) for c,cs in groupby('Pedro Manoel')]
Run Code Online (Sandbox Code Playgroud)

输出:

[('P', 1), ('e', 1), ('d', 1), ('r', 1), ('o', 1), (' ', 1), ('M', 1), ('a', 1), ('n', 1), ('o', 1), ('e', 1), ('l', 1)]
Run Code Online (Sandbox Code Playgroud)

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

name = list('Pedro Manoel')
name.sort()
[(c,len(list(cs))) for c,cs in groupby(name)]
Run Code Online (Sandbox Code Playgroud)

输出:

[(' ', 1), ('M', 1), ('P', 1), ('a', 1), ('d', 1), ('e', 2), ('l', 1), ('n', 1), ('o', 2), ('r', 1)]
Run Code Online (Sandbox Code Playgroud)

只记得,如果列表没有排序,groupby函数将无法正常工作!

  • 实际上它有效.您可能认为此行为已损坏,但在某些情况下它很有用.请参阅此问题的答案以获取示例:http://stackoverflow.com/questions/1553275/how-to-strip-a-list-of-tuple-with-python (7认同)

小智 6

排序和分组

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'}
Run Code Online (Sandbox Code Playgroud)


Aar*_*all 5

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

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

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

下面是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))
Run Code Online (Sandbox Code Playgroud)

版画

[(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]
Run Code Online (Sandbox Code Playgroud)


Ank*_*pta 5

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)
Run Code Online (Sandbox Code Playgroud)

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

例子:

l = [2, 1, 2, 3, 1, 3, 2, 1, 3, 3, 1, 3, 2, 3, 1, 2, 1, 3, 2, 3]
Run Code Online (Sandbox Code Playgroud)

应用 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]
Run Code Online (Sandbox Code Playgroud)

一旦使用了如上所示的字典,就会得出以下结果,可以轻松迭代:

{2: [2, 2, 2, 2, 2, 2], 1: [1, 1, 1, 1, 1, 1], 3: [3, 3, 3, 3, 3, 3, 3, 3]}
Run Code Online (Sandbox Code Playgroud)

  • 请提供关于此代码如何回答问题的解释(字面上是要求_如何_使用“groupby”)。此外,该代码存在缩进错误。 (5认同)

归档时间:

查看次数:

264508 次

最近记录:

5 年,10 月 前