2010-08-16 667 views
31

En Python, que tienen lista de dicts:Cómo sumar elementos dict

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

Quiero una última dict que contendrá la suma de todos predice. Es decir, el resultado será: {'a':5, 'b':7}

N.B: cada dict de la lista contendrá el mismo número de pares clave y valor.

+1

Python? Tu intento? –

Respuesta

15

Un poco feo, pero una sola línea:

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

en realidad tengo una lista de objetos y este diccionario es una propiedad del objeto, ¿tiene alguna solución? :( –

+0

No sé a qué te refieres? – carl

+1

[ob1, ob2, ob3] ... cada objeto tiene un dato de propiedad ob1.data que devuelve un dict {'a': 2, 'b': 3} así –

5

El siguiente código muestra una forma de hacerlo:

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 

Este salidas:

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

como usted deseado.

O, como inspirado por Kriss, mejor, pero todavía legible:

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 

I de pino para los días del original Python, legible :-)

+0

Puede simplificar el primer bucle 'for' en' final = {}. fromkeys (dict1 [0], 0) '. ¿o es eso lo que se está "leyendo"? :) –

+0

Podría simplificar todo el asunto en la respuesta de Carl pero eso significaría (1) que también podría borrar mi respuesta; y (2) No podría leerlo el próximo mes cuando descubrí que necesitaba un pequeño cambio :-) Debo mencionar que utilizo Python para la enseñanza (_my_ marca de Python en lugar de la marca de carl). Realmente es un buen lenguaje para enseñar los conceptos básicos (secuencia, iteración, selección) a los niños pero, si vas a golpearlos en la cabeza con lambdas y demás, también puedes enseñarles F # o Haskell. – paxdiablo

+0

@paxdiablo: para la legibilidad, puede eliminar completamente el bucle de inicio, simplemente reemplace '+ d [k]' con '+ res.get (k, 0)' – kriss

7

Esto podría ayudar:

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

Apalancamiento sum() debe obtener un mejor rendimiento al agregar más de unos pocos 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} 

añadiendo la sugerencia de 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} 

creo que la versión de Stephan del código Python2.7 lee muy bien

+2

¿Hay alguna razón por la que use 'map' y' itemgetter' en lugar de lista de comprensión en el ciclo interno (es decir 'dict ((k, suma (d [k] para d en dict1)) para k en dict1 [0]) ')? – stephan

+0

@stephan, solía ser más rápido ... parece ser más o menos la misma velocidad ahora. Lo agregaré a mi respuesta –

+0

Gracias, no lo sabía. +1 – stephan

39

Puede utilizar el collections.Counter

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

O, si lo prefiere oneliners :

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

o' suma (map (collections.Counter, dict1), Counter()) '. Pero no estoy seguro del rendimiento relativo de las versiones funcionales que crean todos esos 'Contadores()' –

+4

. Esta respuesta demuestra la regla de oro de la programación de Python: si viene incluida con Python, no reinvente la rueda. Un punto: el resultado final 'contador' es una instancia de una subclase de' dict', si el OP quiere un 'dict' simple podría agregar un' contador = dict (contador) 'final. – Duncan

+1

+1 para Python !!! – mVChr

1

En Python 2.7 puede reemplazar el dict con un objeto collections.Counter. Esto admite sumas y restas de Contadores.

+0

pero es para la versión 2.7 –

+0

Y en Python 3? –

4

Me interesaba el rendimiento de los métodos propuestos de contador, reducción y suma para listas grandes. Tal vez alguien más esté interesado en esto también. Puede echar un vistazo aquí: https://gist.github.com/torstenrudolf/277e98df296f23ff921c

probado que los tres métodos de esta lista de diccionarios:

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

el método de la suma mostraron el mejor desempeño, seguido de reducir y Contador fue el más lento. El tiempo mostrado a continuación está en segundos.

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

Pero esto depende del número de elementos en los diccionarios.el método de suma se ralentizará más rápido que el reductor.

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

Aquí hay una hermosa razonable.

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

Una solución adicional una línea

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

Esto crea un solo contador, lo utiliza como un acumulador y, finalmente, se convierte de nuevo a una dict.