2010-08-16 19 views
31

In Python, ho elenco di dicts:Come riassumere gli elementi dict

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}] 

Voglio uno dict finale che conterrà la somma di tutti dicts. I.e il risultato sarà: {'a':5, 'b':7}

N.B: ogni dict nella lista conterrà lo stesso numero di coppie chiave, valore.

+1

Python? Il tuo tentativo? –

risposta

15

un po 'brutto, ma una battuta:

dictf = reduce(lambda x, y: dict((k, v + y[k]) for k, v in x.iteritems()), dict1) 
+0

effettivamente ho un elenco di oggetti e questo dizionario è una proprietà di oggetto, ha qualche soluzione? :( –

+0

Non so cosa intendi? – carl

+1

[ob1, ob2, ob3] .. ogni oggetto ha una proprietà data ob1.data restituisce un dict {'a': 2, 'b': 3} come quello –

5

Il seguente codice mostra un modo per farlo:

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}] 

final = {} 
for k in dict1[0].keys():   # Init all elements to zero. 
    final[k] = 0 
for d in dict1: 
    for k in d.keys(): 
     final[k] = final[k] + d[k] # Update the element. 

print final 

Questo uscite:

{'a': 5, 'b': 7} 

come si desiderato.

Oppure, come ispirato da kriss, meglio, ma ancora leggibile:

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}] 

final = {} 
for d in dict1: 
    for k in d.keys(): 
     final[k] = final.get(k,0) + d[k] 

print final 

ho pino per i giorni della, Python leggibile originale :-)

+0

Puoi semplificare il primo 'for' loop a' final = {}. fromkeys (dict1 [0], 0) '. o è quello che "leggibile" sta ottenendo? :) –

+0

Potrei _semplificare_ l'intera cosa nella risposta di Carl, ma ciò significherebbe (1) Posso anche cancellare la mia risposta; e (2) non sarei in grado di leggerlo il prossimo mese quando ho trovato che avevo bisogno di un piccolo cambiamento :-) Dovrei menzionare che uso Python per insegnare (_my_ marchio di Python piuttosto che il marchio di Carl). È davvero un buon linguaggio per insegnare le basi (sequenza, iterazione, selezione) ai bambini ma, se hai intenzione di colpirli in testa con lambda e simili, potresti anche insegnare loro F # o Haskell. – paxdiablo

+0

@paxdiablo: per la leggibilità, è possibile rimuovere completamente il ciclo di init, basta sostituire '+ d [k]' con '+ res.get (k, 0)' – kriss

7

Questo potrebbe aiutare:

def sum_dict(d1, d2): 
    for key, value in d1.items(): 
     d1[key] = value + d2.get(key, 0) 
    return d1 

>>> dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}] 
>>> reduce(sum_dict, dict1) 
{'a': 5, 'b': 7} 
11

Leveraging sum() dovrebbe ottenere prestazioni migliori quando si aggiungono più di pochi dicts

>>> dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}] 
>>> from operator import itemgetter 
>>> {k:sum(map(itemgetter(k), dict1)) for k in dict1[0]}  # Python2.7+ 
{'a': 5, 'b': 7} 
>>> dict((k,sum(map(itemgetter(k), dict1))) for k in dict1[0]) # Python2.6 
{'a': 5, 'b': 7} 

aggiungendo suggerimento di Stephan

>>> {k: sum(d[k] for d in dict1) for k in dict1[0]}   # Python2.7+ 
{'a': 5, 'b': 7} 
>>> dict((k, sum(d[k] for d in dict1)) for k in dict1[0])  # Python2.6 
{'a': 5, 'b': 7} 

Penso versione di Stephan del codice Python2.7 legge davvero ben

+2

C'è qualche ragione per usare 'map' e' itemgetter' invece della comprensione delle liste nel ciclo interno (cioè 'dict ((k, sum (d [k] per d in dict1)) per k in dict1 [0]) ')? – stephan

+0

@stephan, era più veloce ... sembra essere della stessa velocità ora. Lo aggiungerò alla mia risposta –

+0

Grazie, non lo sapevo. +1 – stephan

39

È possibile utilizzare il collections.Counter

counter = collections.Counter() 
for d in dict1: 
    counter.update(d) 

O, se si preferisce oneliners :

functools.reduce(operator.add, map(collections.Counter, dict1)) 
+1

o' sum (map (collections.Counter, dict1), Counter()) '. Ma non sono sicuro delle prestazioni relative delle versioni funzionali che creano tutti quei 'Counters()' –

+4

Questa risposta dimostra la regola d'oro della programmazione Python: se viene inclusa con Python non reinventare la ruota. Un punto: il risultato finale 'counter' è un'istanza di una sottoclasse di' dict', se l'OP vuole un semplice 'dict', potrebbe aggiungere un' counter = dict (counter) finale '. – Duncan

+1

+1 per Python !!! – mVChr

1

In Python 2.7 è possibile sostituire il dict con un oggetto collections.Counter. Questo supporta l'addizione e la sottrazione dei contatori.

+0

ma per la versione 2.7 –

+0

E in Python 3? –

4

Mi interessava l'esecuzione del contatore proposto, ridurre e sommare i metodi per elenchi di grandi dimensioni. Forse qualcun altro è interessato anche a questo. Si può dare un'occhiata qui: https://gist.github.com/torstenrudolf/277e98df296f23ff921c

ho testato i tre metodi per questa lista di dizionari:

dictList = [{'a': x, 'b': 2*x, 'c': x**2} for x in xrange(10000)] 

il metodo sum ha mostrato le migliori prestazioni, seguito da ridurre e contatore era il più lento. Il tempo mostrato sotto è in secondi.

In [34]: test(dictList) 
Out[34]: 
{'counter': 0.01955194902420044, 
'reduce': 0.006518083095550537, 
'sum': 0.0018319153785705566} 

Ma questo dipende dal numero di elementi nei dizionari.il metodo della somma rallenta più velocemente della riduzione.

l = [{y: x*y for y in xrange(100)} for x in xrange(10000)] 

In [37]: test(l, num=100) 
Out[37]: 
{'counter': 0.2401433277130127, 
'reduce': 0.11110662937164306, 
'sum': 0.2256883692741394} 
2

Ecco un ragionevole bello.

final = {} 
for k in dict1[0].Keys(): 
    final[k] = sum(x[k] for x in dict1) 
return final 
0

Un'ulteriore soluzione una linea

dict(
    functools.reduce(
     lambda x, y: x.update(y) or x, # update, returns None, and we need to chain. 
     dict1, 
     collections.Counter()) 
) 

Questo crea un solo contatore, utilizza come un accumulatore e infine riconverte il segnale di un dizionario.