用于各种分组的首选数据结构是
dict.这个想法是使用唯一标识组的东西作为dict的密钥,并将属于同一组的所有值存储在同一个密钥下.
例如,您的数据可以存储在这样的dict中:
{1: ['a', 'b'],
2: ['x']}
您用于对值进行分组的整数用作dict键,值将在列表中聚合.
我们使用dict的原因是因为它可以在恒定的O(1)时间内将键映射到值.这使得分组过程非常有效并且也非常容易.对于所有类型的分组任务,代码的一般结构将始终相同:您迭代数据并逐渐填充具有分组值的dict.使用defaultdict而不是常规字典会使整个过程变得更加容易,因为我们不必担心使用空列表初始化dict.
import collections
groupdict = collections.defaultdict(list)
for value in data:
group = value[0]
value = value[1]
groupdict[group].append(value)
# result:
# {1: ['a', 'b'],
# 2: ['x']}
将数据分组后,剩下的就是将dict转换为所需的输出格式:
result = [(key, ''.join(values)) for key, values in groupdict.items()]
# result: [(1, 'ab'), (2, 'x')]
分组食谱
以下部分将提供不同类型的输入和输出的配方,并显示如何按各种事物分组.一切的基础是以下片段:
import collections
groupdict = collections.defaultdict(list)
for value in data: # input
group = ??? # group identifier
value = ??? # value to add to the group
groupdict[group].append(value)
result = groupdict # output
每个注释行都可以/必须根据您的使用情况进行自定义.
输入
输入数据的格式决定了如何迭代它.
在本节中,我们将自定义数据中的for值:配方行.
>值列表
通常,所有值都存储在一个平面列表中:
data = [value1, value2, value3, ...]
在这种情况下,我们只需使用for循环遍历列表:
for value in data:
>多个清单
如果您有多个列表,每个列表包含不同属性的值,例如
firstnames = [firstname1, firstname2, ...]
middlenames = [middlename1, middlename2, ...]
lastnames = [lastname1, lastname2, ...]
使用zip函数同时迭代所有列表:
for value in zip(firstnames, middlenames, lastnames):
这将使值成为(名字,中间名,姓氏)的元组.
>多个dicts或dicts列表
如果你想结合多个dicts
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 5}
首先将它们全部放在一个列表中:
dicts = [dict1, dict2]
然后使用两个嵌套循环迭代所有(键,值)对:
for dict_ in dicts:
for value in dict_.items():
在这种情况下,value变量将采用2元素元组的形式,如(‘a’,1)或(‘b’,2).
分组
在这里,我们将介绍从数据中提取组标识符的各种方法.
在本节中,我们将自定义组= ???食谱线.
>按list / tuple / dict元素分组
如果您的值是列表或元组(如attr1,attr2,attr3,…),并且您希望按第n个元素对它们进行分组:
group = value[n]
dicts的语法是相同的,所以如果你有{‘firstname’:’foo’,’lastname’:’bar’}这样的值,你想按名字分组:
group = value['firstname']
>按属性分组
如果您的值是像datetime.date(2018,5,27)这样的对象,并且您希望按属性对它们进行分组,例如年份:
group = value.year
>按键功能分组
有时你有一个函数在调用时返回一个值的组.例如,您可以使用len函数按其长度对值进行分组:
group = len(value)
>按多个值分组
如果您希望按多个值对数据进行分组,则可以使用tuple作为组标识符.例如,要按字符串的首字母和长度对字符串进行分组:
group = (value[0], len(value))
>按不可用的东西分组
因为dict键必须是hashable,所以如果尝试按无法散列的内容进行分组,则会遇到问题.在这种情况下,您必须找到将不可消息值转换为可哈希表示的方法.
> sets:将设置转换为frozensets,可以清除:
group = frozenset(group)
> dicts:Dicts可以表示为排序(键,值)元组:
group = tuple(sorted(group.items()))
修改聚合值
有时您会想要修改您正在分组的值.例如,如果您按第一个元素对(1,’a’)和(1,’b’)这样的元组进行分组,则可能需要从每个元组中删除第一个元素以获得类似{1:[ ‘a’,’b’]}而不是{1:[(1,’a’),(1,’b’)]}.
在本节中,我们将自定义值= ???食谱线.
>没有变化
如果您不想以任何方式更改值,只需删除值= ???你的代码中的一行.
>只保留一个列表/元组/字典元素
如果您的值是[1,’a’]之类的列表,并且您只想保留’a’:
value = value[1]
或者,如果它们像{‘firstname’:’foo’,’lastname’:’bar’}那样,你只想保留名字:
value = value['firstname']
>删除第一个列表/元组元素
如果您的值是[1,’a’,’foo’]和[1,’b’,’bar’]之类的列表,并且您想要丢弃每个元组的第一个元素以获得类似[[‘a’的组,’foo],[‘b’,’bar’]],使用切片语法:
value = value[1:]
>删除/保留任意列表/元组/字典元素
如果您的值是[‘foo’,’bar’,’baz’]之类的列表,或者像”firstname’:’foo’,’middlename’:’bar’,’lastname’:’baz’}这样的列表,你想要的删除或保留其中一些元素,首先要创建要保留或删除的元素的set.例如:
indices_to_keep = {0, 2}
keys_to_delete = {'firstname', 'middlename'}
然后从此列表中选择相应的代码段:
>保留列表元素:value = [val for i,val in enumerate(value)if i in indices_to_keep]
>要删除列表元素:value = [val for i,val in enumerate(value)if i not in indices_to_delete]
>保留dict元素:value = {key:val表示key,val表示value.items()if key in key_to_keep]
>要删除dict元素:value = {key:val表示key,val表示value.items(),如果key不在keys_to_delete中]
产量
分组完成后,我们有一个填充列表的defaultdict.但是期望的结果并不总是(默认)dict.
在本节中,我们将自定义配方的result = groupdict行.
>一个普通的词典
要将defaultdict转换为常规字典,只需在其上调用dict构造函数:
result = dict(groupdict)
>(组,值)对的列表
要从dict {group1:[value1,value2],group2:[value3]}获得[[group1,value1],(group1,value2),(group2,value3)]的结果,请使用list comprehension:
result = [(group, value) for group, values in groupdict.items()
for value in values]
>仅包含值的嵌套列表
要从dict {group1:[value1,value2],group2:[value3]}获得[[value1,value2],[value3]]的结果,请使用dict.values:
result = list(groupdict.values())
>只是值的平面列表
要从dict {group1:[value1,value2],group2:[value3]}获得[value1,value2,value3]的结果,请使用list comprehension将dict展平:
result = [value for values in groupdict.values() for value in values]
>展平可迭代值
如果您的值是列表或其他迭代类似的
groupdict = {group1: [[list1_value1, list1_value2], [list2_value1]]}
你想要一个扁平化的结果
result = {group1: [list1_value1, list1_value2, list2_value1]}
你有两个选择:
result = {group: [x for iterable in values for x in iterable]
for group, values in groupdict.items()}
>首先使用list.extend而不是list.append来避免创建可迭代列表.换句话说,更改
groupdict[group].append(value)
至
groupdict[group].extend(value)
然后只需设置result = groupdict.
>排序列表
Dicts是无序数据结构.如果你遍历一个字典,你永远不知道它的元素将被列出的顺序.如果您不关心订单,可以使用上面显示的配方.但是如果您关心订单,则必须相应地对输出进行排序.
我将使用以下dict来演示如何以各种方式对输出进行排序:
groupdict = {'abc': [1], 'xy': [2, 5]}
请记住,这是一个元组合,可能需要与此答案的其他部分结合才能获得您想要的输出.一般的想法是在使用字典键从dict中提取值之前对字典键进行排序:
groups = sorted(groupdict.keys())
# groups = ['abc', 'xy']
请记住,如果要自定义排序顺序,sorted接受关键功能.例如,如果dict键是字符串,并且您希望按长度对它们进行排序:
groups = sorted(groupdict.keys(), key=len)
# groups = ['xy', 'abc']
对密钥进行排序后,使用它们以正确的顺序从dict中提取值:
# groups = ['abc', 'xy']
result = [groupdict[group] for group in groups]
# result = [[1], [2, 5]]
请记住,这可以与此答案的其他部分结合使用,以获得不同类型的输出.例如,如果要保留组标识符:
# groups = ['abc', 'xy']
result = [(group, groupdict[group]) for group in groups]
# result = [('abc', [1]), ('xy', [2, 5])]
为方便起见,以下是一些常用的排序顺序:
>按每组的值数排序:
groups = sorted(groudict.keys(), key=lambda group: len(groupdict[group]))
result = [groupdict[group] for group in groups]
# result = [[2, 5], [1]]
>计算每组中的值的数量
要计算与每个组关联的元素数,请使用len函数:
result = {group: len(values) for group, values in groupdict.items()}
如果要计算不同元素的数量,请使用set来消除重复项:
result = {group: len(set(values)) for group, values in groupdict.items()}
一个例子
为了演示如何将此配方中的工作解决方案拼凑起来,让我们尝试转换输入
data = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
成
result = [["A", "C"], ["B"], ["D", "E"]]
换句话说,我们按照第二个元素对列表进行分组.
配方的前两行总是相同的,所以让我们先复制一下:
import collections
groupdict = collections.defaultdict(list)
现在我们必须找出如何循环输入.由于我们的输入是一个简单的值列表,因此正常的for循环就足够了:
for value in data:
接下来,我们必须从值中提取组标识符.我们按第二个列表元素进行分组,因此我们使用索引:
group = value[1]
下一步是转换价值.由于我们只想保留每个列表的第一个元素,我们再次使用列表索引:
value = value[0]
最后,我们必须弄清楚如何将我们生成的字典转换为列表.我们想要的是一个没有组的值列表.我们查阅配方的输出部分以找到合适的dict flattening片段:
result = list(groupdict.values())
Etvoilà:
data = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
import collections
groupdict = collections.defaultdict(list)
for value in data:
group = value[1]
value = value[0]
groupdict[group].append(value)
result = list(groupdict.values())
# result: [["A", "C"], ["B"], ["D", "E"]]