2010-09-20 7 views
74

Angenommen, dass ich ein solcher Satz von Paar Daten haben, wo der Index 0 ist der Wert und der Index 1 ist der Typ:Python Gruppe von

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

ich gruppieren wollen, dass sie durch ihren Typ (vom ersten indiziert String) als solcher:

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

Wie kann ich dies auf effiziente Weise erreichen?

Danke

Antwort

104

Tun Sie es in 2 Schritten. Erstellen Sie zuerst ein Wörterbuch.

>>> 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) 
... 

Dann konvertieren Sie das Wörterbuch in das erwartete Format.

>>> [{'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'}] 

Es ist auch mit itertools.groupby möglich, aber es erfordert die Eingabe zunächst sortiert werden.

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

Hinweis beides nicht respektiert die ursprüngliche Reihenfolge der Tasten. Sie benötigen ein OrderedDict, wenn Sie die Bestellung behalten möchten.

>>> 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

einstellen, wie dies, wenn die Eingangstupel einen Schlüssel und zwei oder mehr Werte hat getan werden kann, wie folgt aus: '[(‚11013331‘,‚rot‘ , 'KAT'), ('9085267', 'blue' 'KAT')] 'wo das letzte Element des Tupels der Schlüssel ist und die ersten beiden als Wert. Ergebnis sollte so sein: result = [{ Typ: 'KAT', Elemente: [('11013331', rot), ('9085267', blau)]}] – user1144616

38

Python eingebauten in itertools Modul hat tatsächlich eine groupby Funktion, die Sie verwenden können, aber die Elemente müssen zuerst so sortiert werden, gruppiert werden, dass die Elemente angrenzen, in der Liste gruppiert werden:

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) 

Jetzt sieht Eingang wie:

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

groupby eine Folge von 2-Tupeln zurückgibt, von der Form (key, values_iterator). Wir wollen dies in eine Liste von Dicts umwandeln, in der der "Typ" der Schlüssel ist, und "Items" ist eine Liste der 0. Elemente der Tupel, die von values_iterator zurückgegeben werden. Wie folgt aus:

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))) 

Jetzt result enthält gewünschten dict, wie in Ihrer Frage angegeben.

Sie könnten jedoch in Erwägung ziehen, nur ein einziges Diktat daraus zu erstellen, das nach Typ und jedem Wert mit der Werteliste codiert ist. Um in Ihrem aktuellen Formular die Werte für einen bestimmten Typ zu finden, müssen Sie über die Liste iterieren, um das dict zu finden, das den passenden 'type' Schlüssel enthält, und dann das Element 'items' daraus abfragen. Wenn Sie ein einzelnes Diktat anstelle einer Liste von 1-Item-Dicts verwenden, können Sie die Items für einen bestimmten Typ mit einer einzigen Schlüsselsuche im Master-Diktat suchen.Mit groupby, würde dies wie folgt aussehen:

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

result jetzt dieses dict enthält (dies ist ähnlich dem Zwischen res defaultdict in @ KennyTM Antwort):

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

(Wenn Sie wollen, dass diese zu reduzieren, ein Einzeiler, können Sie:

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

oder das neumodische dict Verständnis Formular:

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

Die folgende Funktion schnell wird (keine Sortierung erforderlich) Gruppe Tupel beliebiger Länge durch einen Schlüssel mit einem beliebigen Index:

# 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 

Bei Ihrer Frage, den Index des Schlüssels Sie mögen von zu Gruppe 1, also:

group_by(input,1) 

gibt

{'ETH': ('5238761','5349618','962142','7795297','7341464','5594916','1550003'), 
'KAT': ('11013331', '9843236'), 
'NOT': ('9085267', '11788544')} 

Dies ist nicht genau die Ausgabe, nach der Sie gefragt haben, könnte aber auch Ihren Anforderungen entsprechen.

0

Ich mochte auch Pandas einfach grouping. es ist leistungsstark, einfach und angemessen für große Daten

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

Verwandte Themen