2010-09-20 60 views
74

假設我有一個這樣的集合對DATAS其中索引0是值和索引1的是類型:Python的組由

input = [ 
      ('11013331', 'KAT'), 
      ('9085267', 'NOT'), 
      ('5238761', 'ETH'), 
      ('5349618', 'ETH'), 
      ('11788544', 'NOT'), 
      ('962142', 'ETH'), 
      ('7795297', 'ETH'), 
      ('7341464', 'ETH'), 
      ('9843236', 'KAT'), 
      ('5594916', 'ETH'), 
      ('1550003', 'ETH') 
     ] 

我想將它們按它們的類型(由第一索引字符串):

result = [ 
      { 
      type:'KAT', 
      items: ['11013331', '9843236'] 
      }, 
      { 
      type:'NOT', 
      items: ['9085267', '11788544'] 
      }, 
      { 
      type:'ETH', 
      items: ['5238761', '962142', '7795297', '7341464', '5594916', '1550003'] 
      } 
     ] 

如何以高效的方式實現此目的?

謝謝

回答

104

分兩步做。首先,創建一個字典。

>>> 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但它需要輸入事先排好序。

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

如何才能做到這一點,如果輸入數組有一個鍵和兩個或多個值,如:'[(「11013331」,「紅」 ,'KAT'),('9085267','blue''KAT')]'元組的最後一個元素是關鍵字,前兩個元素是值。結果應該是這樣的: result = ['KAT',items:[('11013331',red),('9085267',blue)]}] – user1144616 2012-03-06 18:52:02

38

Python的內置itertools模塊實際上有一個groupby功能,您可以使用,但要進行分組的元素必須先進行排序,從而進行分組的元素在列表中是連續的:

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) 

現在輸入的樣子:形式的

[('5238761', 'ETH'), ('5349618', 'ETH'), ('962142', 'ETH'), ('7795297', 'ETH'), 
('7341464', 'ETH'), ('5594916', 'ETH'), ('1550003', 'ETH'), ('11013331', 'KAT'), 
('9843236', 'KAT'), ('9085267', 'NOT'), ('11788544', 'NOT')] 

groupby返回的2元組的序列,。我們想要的是把它變成一個「類型」是關鍵字的字典列表,而「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包含您所需的字典,在你的問題說明。

但是,您可能會考慮,只是將一個單詞作爲輸入,按類型鍵入,每個值都包含值列表。在你當前的表單中,爲了找到某個特定類型的值,你需要迭代列表來查找包含匹配'type'鍵的dict,然後從中獲取'items'元素。如果您使用單個詞典而不是單項詞典列表,則可以通過在主詞典中進行單鍵查找來查找特定類型的項目。使用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']} 

(如果你想將這一數字減少到一行代碼,您可以:

result = dict((key,list(v[0] for v in valuesiter) 
       for key,valuesiter in groupby(input, key=sortkeyfn)) 

或使用新奇的字典-理解的形式:

result = {key:list(v[0] for v in valuesiter) 
       for key,valuesiter in groupby(input, key=sortkeyfn)} 
1

下面的函數將迅速(沒有排序需要)組中的任何長度的一個關鍵的元組有任何索引:

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

這不完全是您要求的輸出,但可能會滿足您的需求。

0

我也喜歡熊貓簡單grouping。它的功能強大,操作簡單,最充足的大型數據集

result = pandas.DataFrame(input).groupby(1).groups