2010-07-29 16 views
6

Ich habe eine sehr lange Liste von Wörterbüchern mit String-Indizes und Integer-Werten. Viele der Schlüssel sind in den Wörterbüchern gleich, wenn auch nicht alle. Ich möchte ein Wörterbuch erstellen, in dem die Schlüssel die Vereinigung der Schlüssel in den einzelnen Wörterbüchern sind und die Werte die Summe aller Werte sind, die diesem Schlüssel in jedem der Wörterbücher entsprechen. (Der Wert für den Schlüssel 'apple' im kombinierten Wörterbuch ist beispielsweise die Summe des Werts von 'apple' in der ersten, plus der Summe des Werts von 'apple' in der zweiten usw.)Hinzufügen von Elementen in einer Liste von Wörterbüchern

Ich habe folgendes, aber es ist ziemlich umständlich und dauert Alter auszuführen. Gibt es einen einfacheren Weg, um das gleiche Ergebnis zu erzielen?

comb_dict = {} 
for dictionary in list_dictionaries: 
    for key in dictionary: 
     comb_dict.setdefault(key, 0) 
     comb_dict[key] += dictionary[key] 
return comb_dict 

Antwort

9

Hier sind einige Microbenchmarks, die vorschlagen, f2 (siehe unten) könnte eine Verbesserung sein. f2 verwendet iteritems, die Ihnen eine zusätzliche dict-Lookup in der inneren Schleife vermeiden erlaubt:

import collections 
import string 
import random 

def random_dict(): 
    n=random.randint(1,26) 
    keys=list(string.letters) 
    random.shuffle(keys) 
    keys=keys[:n] 
    values=[random.randint(1,100) for _ in range(n)]  
    return dict(zip(keys,values)) 

list_dictionaries=[random_dict() for x in xrange(100)] 

def f1(list_dictionaries): 
    comb_dict = {} 
    for dictionary in list_dictionaries: 
     for key in dictionary: 
      comb_dict.setdefault(key, 0) 
      comb_dict[key] += dictionary[key] 
    return comb_dict 

def f2(list_dictionaries):  
    comb_dict = collections.defaultdict(int) 
    for dictionary in list_dictionaries: 
     for key,value in dictionary.iteritems(): 
      comb_dict[key] += value 
    return comb_dict 

def union(dict_list): 
    all_keys = set() 
    for d in dict_list: 
     for k in d: 
      all_keys.add(k) 
    for key in all_keys: 
     yield key, sum(d.get(key,0) for d in dict_list) 

def f3(list_dictionaries): 
    return dict(union(list_dictionaries)) 

Hier sind die Ergebnisse:

% python -mtimeit -s"import test" "test.f1(test.list_dictionaries)" 
1000 loops, best of 3: 776 usec per loop 
% python -mtimeit -s"import test" "test.f2(test.list_dictionaries)" 
1000 loops, best of 3: 432 usec per loop  
% python -mtimeit -s"import test" "test.f3(test.list_dictionaries)" 
100 loops, best of 3: 2.19 msec per loop 
+0

Danke! f2() hat ungefähr 80% der Zeit für meine spezielle Anwendung gekürzt. YRMV, offensichtlich. – chimeracoder

1

Dies könnte auch schnell sein, aber es hängt wirklich von Ihren Daten ab. Es vermeidet alle wechselnden dicts oder zusätzliche Listen - nur einen Satz aller Schlüssel und viel liest :-)

from itertools import chain 

def union(dict_list): 
    all_keys = set(chain.from_iterable(dict_list)) 
    for key in all_keys: 
     yield key, sum(d.get(key,0) for d in dict_list) 

combined = dict(union(dict_list)) 
+0

Obwohl diese anspruchsvollere Funktionen verwendet, kann ich mir nicht vorstellen Das wird schneller (aber ich könnte falsch liegen). Im OP-Code wird die Liste der Wörterbücher nur einmal durchlaufen, also auch jedes Wörterbuch. In Ihrem Code wird jedes Wörterbuch einmal durchlaufen, um den Satz von Schlüsseln zu erstellen, und dann wird die Liste der Diktate "# all_keys" durchlaufen. –

+0

Felix Kling: Nun, ich habe gerade versucht, wenn ich einen Iterator hinzufüge (siehe Revisionen ;-), um nur einmal zu durchlaufen, wenn es noch langsamer wird. Ratet mal, dass das extra hasing vom Set das Problem ist. –

0

Sie konnten einige Anregungen aus dem Google-Karte-reduzieren nehmen. Von dem, was ich verstehe, wurde entwickelt, um genau diese Art von Problem zu lösen.

Verwandte Themen