Python组合
假设我有一组这样的数据对,其中索引0是数值,索引1是数据types:
input = [ ('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'), ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH') ]
我想按他们的types(按第一个索引string)将它们分组:
result = [ { type:'KAT', items: ['11013331', '9843236'] }, { type:'NOT', items: ['9085267', '11788544'] }, { type:'ETH', items: ['5238761', '962142', '7795297', '7341464', '5594916', '1550003'] } ]
我怎样才能以有效的方式实现这一点?
谢谢
分两步进行。 首先,创build一个字典。
>>> input = [('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'), ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH')] >>> from collections import defaultdict >>> res = defaultdict(list) >>> for v, k in input: res[k].append(v) ...
然后,将该字典转换为预期的格式。
>>> [{'type':k, 'items':v} for k,v in res.items()] [{'items': ['9085267', '11788544'], 'type': 'NOT'}, {'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'items': ['11013331', '9843236'], 'type': 'KAT'}]
itertools.groupby也是可能的,但是它需要input先被sorting。
>>> sorted_input = sorted(input, key=itemgetter(1)) >>> groups = groupby(sorted_input, key=itemgetter(1)) >>> [{'type':k, 'items':[x[0] for x in v]} for k, v in groups] [{'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'items': ['11013331', '9843236'], 'type': 'KAT'}, {'items': ['9085267', '11788544'], 'type': 'NOT'}]
注意这两个都不尊重键的原始顺序。 如果您需要保留订单,您需要OrderedDict。
>>> from collections import OrderedDict >>> res = OrderedDict() >>> for v, k in input: ... if k in res: res[k].append(v) ... else: res[k] = [v] ... >>> [{'type':k, 'items':v} for k,v in res.items()] [{'items': ['11013331', '9843236'], 'type': 'KAT'}, {'items': ['9085267', '11788544'], 'type': 'NOT'}, {'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}]
Python的内置itertools
模块实际上有一个你可以使用的groupby
函数,但是要分组的元素必须首先被sorting,这样在列表中要分组的元素才是连续的:
sortkeyfn = key=lambda s:s[1] input = [('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'), ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH')] input.sort(key=sortkeyfn)
现在input如下所示:
[('5238761', 'ETH'), ('5349618', 'ETH'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('5594916', 'ETH'), ('1550003', 'ETH'), ('11013331', 'KAT'), ('9843236', 'KAT'), ('9085267', 'NOT'), ('11788544', 'NOT')]
groupby
返回forms为(key, values_iterator)
的2元组序列。 我们想要的是把它变成一个“types”是关键的字典列表,而“items”是由values_iterator返回的元组的第0个元素的列表。 喜欢这个:
from itertools import groupby result = [] for key,valuesiter in groupby(input, key=sortkeyfn): result.append(dict(type=key, items=list(v[0] for v in valuesiter)))
现在result
包含你想要的字典,如你的问题所述。
不过,你可能会考虑一下,只是把一个单词作为input,按types键入,每个值都包含值的列表。 在你现在的forms中,为了find某个特定types的值,你必须迭代列表才能find包含匹配'type'键的dict,然后从中获取'items'元素。 如果您使用单个词典而不是单项词典列表,则可以通过在主词典中单键查找来查找特定types的项目。 使用groupby
,这看起来像:
result = {} for key,valuesiter in groupby(input, key=sortkeyfn): result[key] = list(v[0] for v in valuesiter)
result
现在包含这个字典(这与@ KennyTM的答案中的中间res
defaultdict类似):
{'NOT': ['9085267', '11788544'], 'ETH': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'KAT': ['11013331', '9843236']}
(如果你想减less这一行,你可以:
result = dict((key,list(v[0] for v in valuesiter) for key,valuesiter in groupby(input, key=sortkeyfn))
或者使用新颖的字典理解forms:
result = {key:list(v[0] for v in valuesiter) for key,valuesiter in groupby(input, key=sortkeyfn)}
以下函数将快速( 无需sorting )通过具有任何索引的键对任何长度的元组进行组化:
# given a sequence of tuples like [(3,'c',6),(7,'a',2),(88,'c',4),(45,'a',0)], # returns a dict grouping tuples by idx-th element - with idx=1 we have: # if merge is True {'c':(3,6,88,4), 'a':(7,2,45,0)} # if merge is False {'c':((3,6),(88,4)), 'a':((7,2),(45,0))} def group_by(seqs,idx=0,merge=True): d = dict() for seq in seqs: k = seq[idx] v = d.get(k,tuple()) + (seq[:idx]+seq[idx+1:] if merge else (seq[:idx]+seq[idx+1:],)) d.update({k:v}) return d
在你的问题的情况下,您要分组的键是1,因此:
group_by(input,1)
给
{'ETH': ('5238761','5349618','962142','7795297','7341464','5594916','1550003'), 'KAT': ('11013331', '9843236'), 'NOT': ('9085267', '11788544')}
这不完全是你要求的输出,但也可能适合你的需求。
我也喜欢pandas简单的分组 。 它对于大型数据集是强大的,简单的和最充分的
result = pandas.DataFrame(input).groupby(1).groups