如何使用 itertools.groupby()?

2024-12-02 08:41:00
admin
原创
206
摘要:问题描述:我找不到关于如何实际使用 Pythonitertools.groupby()函数的易懂解释。我想做的是:列出一个列表 - 在本例中,是对象化lxml元素的子元素根据一些标准将其分成几组然后稍后分别对每个组进行迭代。我已经查看过文档,但是在尝试将它们应用到简单的数字列表之外时遇到了困难。那么,我该如何...

问题描述:

我找不到关于如何实际使用 Pythonitertools.groupby()函数的易懂解释。我想做的是:

  • 列出一个列表 - 在本例中,是对象化lxml元素的子元素

  • 根据一些标准将其分成几组

  • 然后稍后分别对每个组进行迭代。

我已经查看过文档,但是在尝试将它们应用到简单的数字列表之外时遇到了困难。

那么,我该如何使用itertools.groupby()?我是否应该使用其他技术?如果能提供指向良好“先决条件”阅读材料的提示,我将不胜感激。


解决方案 1:

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


我不明白的是,在示例构造中

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

这将给你输出:

动物:熊和鸭子。

植物:仙人掌。

车辆:快艇和校车。

解决方案 2:

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

用途

  • 字谜(见笔记本)

  • 分箱

  • 奇数和偶数的分组

  • 按值对列表进行分组

  • 删除重复元素

  • 查找数组中重复元素的索引

  • 将数组拆分成 n 个大小的块

  • 在两个列表之间查找对应元素

  • 压缩算法(参见笔记本)/行程长度编码

  • 按长度、按键功能对字母进行分组(参见笔记本)

  • 超过阈值的连续值(参见笔记本)

  • 查找列表或连续项中的数字范围(参见文档)

  • 查找所有相关的最长序列

  • 采取满足条件的连续序列(参见相关文章)

注意:后面几个例子来自 Víctor Terrón 的 PyCon (talk) (西班牙语),“用 Itertools 在黎明练功”。另请参阅用 C 编写的groupby 源代码。

  • 传递并比较所有项的函数,从而影响结果。其他具有关键功能的对象包括 sorted()max()min()


回复

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

解决方案 3:

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方法实际上只是遍历列表,并且每当键发生变化时它就会创建一个新组。

解决方案 4:

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

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

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

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

解决方案 5:

另一个例子:

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。在这种情况下,首先输出一组零,然后输出一组一,然后再次输出一组零。

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]

解决方案 6:

警告:

语法 list(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功能,同时避免那些令人讨厌的(对于小数据)迭代器。

解决方案 7:

我想再举一个例子,说明不带排序的 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.

有两组携带车辆,而我们预计只有一组

解决方案 8:

遗憾的是,我认为不建议使用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']}

解决方案 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 函数将不起作用

解决方案 10:

排序和分组

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

解决方案 11:

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

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

解决方案 12:

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

您可以使用 groupby 对要迭代的内容进行分组。您为 groupby 提供一个可迭代对象和一个可选的关键函数/可调用对象,通过该函数/可调用对象检查来自可迭代对象的项,它返回一个迭代器,该迭代器提供关键可调用对象的结果和另一个可迭代对象中的实际项的二元组。来自帮助:

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

解决方案 13:

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

解决方案 14:

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

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)

解决方案 15:

要认识到的关键itertools.groupby是,只有当项目在 iterable 中是连续的时,它们才会分组在一起。这就是排序有效的原因,因为基本上您正在重新排列集合,以便现在满足条件的所有项目都callback(item)按顺序出现在排序后的集合中。

话虽如此,您不需要对列表进行排序,您只需要一个键值对的集合,其中值可以根据所产生的每个组迭代而增长groupby。即列表字典。

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

解决方案 16:

这是一个如何groupby直接使用字典列表的示例。

from itertools import groupby
items = [{'app_id': '55222702242335', 'mail_id': '4890770'}, 
{'app_id': '44322702242745', 'mail_id': '4890770'}, 
{'app_id': '80513948813781', 'mail_id': '5083772'}, 
{'app_id': '70514248813211', 'mail_id': '5083772'}]
items.sort(key=lambda x: x['mail_id'])

grouped_items = groupby(items, lambda x:x["mail_id"])
result = {}
for key, item in grouped_items:
    result[key] = list(item)
print(result)

示例输出

{'4890770': [{'app_id': '55222702242335', 'mail_id': '4890770'}, {'app_id': '44322702242745', 'mail_id': '4890770'}], '5083772': [{'app_id': '80513948813781', 'mail_id': '5083772'}, {'app_id': '70514248813211', 'mail_id': '5083772'}]}

或者使用更 Pythonic 的方式

from itertools import groupby
items = [{'app_id': '55222702242335', 'mail_id': '4890770'}, 
{'app_id': '44322702242745', 'mail_id': '4890770'}, 
{'app_id': '80513948813781', 'mail_id': '5083772'}, 
{'app_id': '70514248813211', 'mail_id': '5083772'}]
items.sort(key=lambda x: x['mail_id'])
result = {key: list(group) for key, group in groupby(items, key=lambda x: x['mail_id'])}

print(result)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用