2010-03-12 3 views
3

J'ai corrigé la façon dont le dictionnaire fusionné (all_classes) ci-dessous est créé, mais je me demande s'il peut être plus efficace.Quel est le moyen le plus rapide de fusionner les valeurs de cette structure Python en un seul dictionnaire?

J'ai un dictionnaire de dictionnaires, comme ceci:

groups_and_classes = {'group_1': {'class_A': [1, 2, 3], 
            'class_B': [1, 3, 5, 7], 
            'class_c': [1, 2], # ...many more items like this 
           }, 
         'group_2': {'class_A': [11, 12, 13], 
            'class_C': [5, 6, 7, 8, 9] 
           }, # ...and many more items like this 
        } 

Une fonction crée un nouvel objet de groups_and_classes comme celui-ci (la fonction pour créer cette est souvent appelée):

all_classes = {'class_A': [1, 2, 3, 11, 12, 13], 
       'class_B': [1, 3, 5, 7, 9], 
       'class_C': [1, 2, 5, 6, 7, 8, 9] 
       } 

droit maintenant, il y a une boucle qui fait cela:

all_classes = {} 
for group in groups_and_classes.values(): 
    for c, vals in group.iteritems(): 
     for v in vals: 
      if all_classes.has_key(c): 
       if v not in all_classes[c]: 
        all_classes[c].append(v) 
      else: 
       all_classes[c] = [v] 

jusqu'à présent, je chan ged le code à utiliser un set au lieu d'un list puisque l'ordre de la liste n'a pas d'importance et les valeurs doivent être uniques:

all_classes = {} 
for group in groups_and_classes.values(): 
    for c, vals in group.iteritems(): 
     try: 
      all_classes[c].update(set(vals)) 
     except KeyError: 
      all_classes[c] = set(vals) 

Ceci est un peu plus agréable, et je n'ai pas pour convertir les ensembles en listes en raison de la façon dont all_classes est utilisé dans le code.

Question: Y at-il un moyen plus efficace de créer all_classes (en dehors de la construction en même temps groups_and_classes est construit, et en changeant partout cette fonction est appelée)?

+0

Est-ce le plus grand bottlenec k de vous programme qu'il faut optimiser? Ces micro-optimisations sont rarement nécessaires. Si toutefois vous le faites - le moyen le plus rapide serait de le coder en C. –

Répondre

4

Voici un tweak souci de concision, mais je ne suis pas sûr de la performance:

from collections import defaultdict 
all_classes = defaultdict(set) 
for group in groups_and_classes.values(): 
    for c, vals in group.iteritems(): 
     all_classes[c].update(set(vals)) 

Defaultdicts ne sont pas tout à fait la plus grande chose depuis le pain en tranches, mais ils sont assez cool. :)

+0

Le problème avec defaultdicts IMO est que chaque fois que vous faites référence à une clé qui n'y figure pas, cette clé est ajoutée à la dict même si vous étiez juste obtenir la valeur associée à cette clé (ensemble vide dans ce cas) plutôt que de définir la clé sur une valeur. –

+0

Combinant @ suggestion de Brian ci-dessous avec cette solution, il est le plus rapide sur le 3 (mine et The Machine Charmer étant l'autre 2) –

+0

@justin - bon point. Dans mon cas d'utilisation, je convertis le defaultdict en dict, afin qu'il ne pose pas de problèmes. –

2

Une chose qui pourrait améliorer les choses un peu est d'éviter la conversion redondante à un ensemble, et il suffit d'utiliser:

all_classes[c].update(vals) 

update peut effectivement prendre une itérables arbitraire, car elle essentiellement juste et ajoute itère, Ainsi, vous pouvez éviter une étape de conversion supplémentaire.

+0

Bon conseil - je ne savais pas que –

2

Combining Dictionaries Of Lists In Python.

def merge_dols(dol1, dol2): 
    result = dict(dol1, **dol2) 
    result.update((k, dol1[k] + dol2[k]) for k in set(dol1).intersection(dol2)) 
    return result 

g1 = groups_and_classes['group_1'] 
g2 = groups_and_classes['group_2'] 

all_classes = merge_dols(g1,g2) 

OU

all_classes = reduce(merge_dols,groups_and_classes.values()) 

--copied de Alex Martelli

Si vous obtenez plus de deux groupes vous pouvez alors utiliser itertools.reduce

all_classes = reduce(merge_dols,groups_and_classes.values()) 
Questions connexes