如何使用itertools.groupby()?
我一直找不到一个简单易懂的解释,来说明如何使用Python的itertools.groupby()函数。我想做的事情是:
- 先拿一个列表——在这个例子中,是一个被对象化的
lxml元素的子元素 - 根据某些标准把它分成几个组
- 然后再分别遍历这些组。
我看过官方文档,但在尝试应用这些内容时,除了简单的数字列表外,我遇到了困难。
那么,我该如何使用itertools.groupby()呢?有没有其他的技巧我应该使用?如果能推荐一些好的“基础”阅读材料,我也会很感激。
相关问题:
15 个回答
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 方法实际上只是遍历一个列表,每当关键字变化时,它就会创建一个新的分组。
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 (演讲) (西班牙语),主题是“用 Itertools 的清晨功夫”。还可以查看 groupby 用 C 写的源代码。
* 键函数是一个将所有项目传入并进行比较的函数,从而影响结果。其他有键函数的对象包括 sorted()、max() 和 min()。
回应
# OP: Yes, you can use `groupby`, e.g.
[do_something(list(g)) for _, g in groupby(lxml_elements, criteria_func)]
重要提示:你可能需要先对数据进行排序。
我不太明白的是,在这个示例构造中
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 + ".")
这将给你输出:
动物:熊和鸭子。
植物:仙人掌。
交通工具:快艇和校车。