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