Y a-t-il un moyen pythonique de combiner deux dicts (en ajoutant des valeurs pour les clés qui apparaissent dans les deux)?

par exemple, j'ai deux dicts:

Dict A: {'a': 1, 'b': 2, 'c': 3}
Dict B: {'b': 3, 'c': 4, 'd': 5}

j'ai besoin d'un moyen pythonique de 'combiner' deux dicts tel que le résultat est:

{'a': 1, 'b': 5, 'c': 7, 'd': 5}

, C'est-à-dire: si une clé apparaît dans les deux dicts, ajouter leurs valeurs, si elle apparaît seulement dans une dict, garder sa valeur.

424
demandé sur vaultah 2012-06-13 13:17:28

18 réponses

Utilisation collections.Counter :

>>> from collections import Counter
>>> A = Counter({'a':1, 'b':2, 'c':3})
>>> B = Counter({'b':3, 'c':4, 'd':5})
>>> A + B
Counter({'c': 7, 'b': 5, 'd': 5, 'a': 1})
Les compteurs

sont essentiellement une sous-classe de dict , donc vous pouvez toujours faire tout ce que vous feriez normalement avec ce type, comme itérer sur leurs clés et leurs valeurs.

776
répondu Martijn Pieters 2014-07-04 15:17:12

une solution plus générique, qui fonctionne aussi bien pour les valeurs non numériques:

a = {'a': 'foo', 'b':'bar', 'c': 'baz'}
b = {'a': 'spam', 'c':'ham', 'x': 'blah'}

r = dict(a.items() + b.items() +
    [(k, a[k] + b[k]) for k in set(b) & set(a)])

ou encore plus générique:

def combine_dicts(a, b, op=operator.add):
    return dict(a.items() + b.items() +
        [(k, op(a[k], b[k])) for k in set(b) & set(a)])

par exemple:

>>> a = {'a': 2, 'b':3, 'c':4}
>>> b = {'a': 5, 'c':6, 'x':7}

>>> import operator
>>> print combine_dicts(a, b, operator.mul)
{'a': 10, 'x': 7, 'c': 24, 'b': 3}
109
répondu georg 2016-01-10 02:51:34
>>> A = {'a':1, 'b':2, 'c':3}
>>> B = {'b':3, 'c':4, 'd':5}
>>> c = {x: A.get(x, 0) + B.get(x, 0) for x in set(A).union(B)}
>>> print(c)

{'a': 1, 'c': 7, 'b': 5, 'd': 5}
61
répondu Ashwini Chaudhary 2014-04-01 09:55:46

Intro: Il y a les (probablement) meilleures solutions. Mais vous devez le savoir et vous en souvenir et parfois vous devez espérer que votre version Python n'est pas trop ancienne ou quelque soit le problème.

puis il y a les solutions les plus "hacky". Ils sont grands et court, mais sont parfois difficiles à comprendre, à lire et à retenir.

Il y a, cependant, une alternative qui consiste à essayer de réinventer la roue. - Pourquoi réinventer la roue? - Généralement parce que c'est une très bonne façon d'apprendre (et parfois juste parce que l'outil déjà existant ne fait pas exactement ce que vous voulez et/ou la façon dont vous le souhaitez) et la manière la plus facile si vous ne savez pas ou ne vous souvenez pas de l'outil parfait pour votre problème.

So , je propose de réinventer la roue de la classe Counter du module collections (en partie au moins):

class MyDict(dict):
    def __add__(self, oth):
        r = self.copy()

        try:
            for key, val in oth.items():
                if key in r:
                    r[key] += val  # You can custom it here
                else:
                    r[key] = val
        except AttributeError:  # In case oth isn't a dict
            return NotImplemented  # The convention when a case isn't handled

        return r

a = MyDict({'a':1, 'b':2, 'c':3})
b = MyDict({'b':3, 'c':4, 'd':5})

print(a+b)  # Output {'a':1, 'b': 5, 'c': 7, 'd': 5}

il y aurait probablement d'autres façons de mettre en œuvre cela et il y a déjà des outils pour le faire, mais il est toujours agréable de visualiser comment les choses fonctionneraient essentiellement.

44
répondu JeromeJ 2015-09-26 04:03:33
myDict = {}
for k in itertools.chain(A.keys(), B.keys()):
    myDict[k] = A.get(k, 0)+B.get(k, 0)
12
répondu 2014-07-04 11:01:52

celui avec pas d'importations supplémentaires!

leur est un standard pythonique appelé EAFP (plus facile de demander pardon que la Permission). Le code ci-dessous est basé sur le standard python .

# The A and B dictionaries
A = {'a': 1, 'b': 2, 'c': 3}
B = {'b': 3, 'c': 4, 'd': 5}

# The final dictionary. Will contain the final outputs.
newdict = {}

# Make sure every key of A and B get into the final dictionary 'newdict'.
newdict.update(A)
newdict.update(B)

# Iterate through each key of A.
for i in A.keys():

    # If same key exist on B, its values from A and B will add together and
    # get included in the final dictionary 'newdict'.
    try:
        addition = A[i] + B[i]
        newdict[i] = addition

    # If current key does not exist in dictionary B, it will give a KeyError,
    # catch it and continue looping.
    except KeyError:
        continue

EDIT: merci à jerzyk pour ses suggestions d'amélioration.

11
répondu Devesh Saini 2017-08-12 21:15:31

est certainement la somme de Counter() s est la façon la plus pythonique d'aller dans de tels cas, mais seulement si elle résulte en une valeur positive . Voici un exemple et comme vous pouvez le voir, il n'y a pas de c dans le résultat après la négation de la valeur de c dans le dictionnaire B .

In [1]: from collections import Counter

In [2]: A = Counter({'a':1, 'b':2, 'c':3})

In [3]: B = Counter({'b':3, 'c':-4, 'd':5})

In [4]: A + B
Out[4]: Counter({'d': 5, 'b': 5, 'a': 1})

c'est parce que Counter s ont été principalement conçus pour fonctionner avec des entiers positifs pour représenter des comptes courants (compte négatif est de sens). Mais pour aider avec ces cas d'utilisation,python documente la portée minimale et les restrictions de type comme suit:

  • , Le Compteur de la classe elle-même est un dictionnaire sous-classe sans restrictions sur ses clés et valeurs. Les valeurs sont destinés à être des nombres représentant des comptes, mais vous pouvez stocker quoi que ce soit dans le champ de valeur.
  • la méthode most_common() ne nécessite que que les valeurs soient ordonnables.
  • pour les opérations sur place telles que c[key] += 1 , le type de valeur n'a besoin que du support addition et soustraction. Donc les fractions, les flotteurs et les décimales fonctionneraient et les valeurs négatives sont soutenu. Il en va de même pour update() et subtract() qui permettre négatives et les valeurs zéro pour les entrées et les sorties.
  • les méthodes multiset sont conçues uniquement pour les cas d'utilisation avec des valeurs positives. Les entrées peuvent être négatives ou nulles, mais seuls les résultats positifs les valeurs sont créés. Il n'y a pas de restriction de type, mais la valeur type doit soutenir l'addition, la soustraction, et la comparaison.
  • la méthode elements() nécessite des nombres entiers. Il ignore les comptes zéro et négatif.

donc pour contourner ce problème après avoir sommé votre compteur vous pouvez utiliser Counter.update afin d'obtenir la sortie de désir. Il fonctionne comme dict.update() , mais ajoute compte au lieu de les remplacer.

In [24]: A.update(B)

In [25]: A
Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1})
9
répondu Kasrâmvd 2017-03-10 11:12:15
import itertools
import collections

dictA = {'a':1, 'b':2, 'c':3}
dictB = {'b':3, 'c':4, 'd':5}

new_dict = collections.defaultdict(int)
for k, v in itertools.chain(dictA.iteritems(), dictB.iteritems()):
    new_dict[k] += v

print dict(new_dict)

# OUTPUT
{'a': 1, 'c': 7, 'b': 5, 'd': 5}

ou

Alternative vous pouvez utiliser le compteur comme @Martijn l'a mentionné ci-dessus.

8
répondu Adeel 2014-07-04 10:49:33

Pour une plus générique et extensible cochez la case mergedict . Il utilise singledispatch et peut fusionner des valeurs basées sur ses types.

exemple:

from mergedict import MergeDict

class SumDict(MergeDict):
    @MergeDict.dispatch(int)
    def merge_int(this, other):
        return this + other

d2 = SumDict({'a': 1, 'b': 'one'})
d2.merge({'a':2, 'b': 'two'})

assert d2 == {'a': 3, 'b': 'two'}
6
répondu schettino72 2014-11-14 10:43:41

de plus, veuillez noter que a.update( b ) est 2 fois plus rapide que a + b

from collections import Counter
a = Counter({'menu': 20, 'good': 15, 'happy': 10, 'bar': 5})
b = Counter({'menu': 1, 'good': 1, 'bar': 3})

%timeit a + b;
## 100000 loops, best of 3: 8.62 µs per loop
## The slowest run took 4.04 times longer than the fastest. This could mean that an intermediate result is being cached.

%timeit a.update(b)
## 100000 loops, best of 3: 4.51 µs per loop
3
répondu shouldsee 2017-08-20 15:11:52

de python 3.5: Fusion et sommation

merci à @tokeinizer_fsj qui m'a dit dans un commentaire que je ne comprenais pas complètement le sens de la question (j'ai pensé que add signifiait juste ajouter des clés qui éventuellement étaient différentes dans les deux dictatures et, au lieu de cela, je voulais dire que les valeurs clés communes devraient être résumées). Donc j'ai ajouté cette boucle avant la fusion, de sorte que le second dictionnaire contient la somme des clés communes. Le dernier dictionnaire sera celui dont les valeurs durera dans le nouveau dictionnaire qui est le résultat de la fusion des deux, donc je pense que le problème est résolu. La solution est valide à partir de python 3.5 et des versions suivantes.

a = {
    "a": 1,
    "b": 2,
    "c": 3
}

b = {
    "a": 2,
    "b": 3,
    "d": 5
}

# Python 3.5

for key in b:
    if key in a:
        b[key] = b[key] + a[key]

c = {**a, **b}
print(c)

>>> c
{'a': 3, 'b': 5, 'c': 3, 'd': 5}

code réutilisable

a = {'a': 1, 'b': 2, 'c': 3}
b = {'b': 3, 'c': 4, 'd': 5}


def mergsum(a, b):
    for k in b:
        if k in a:
            b[k] = b[k] + a[k]
    c = {**a, **b}
    return c


print(mergsum(a, b))
3
répondu Giovanni Gianni 2018-07-08 05:24:54
def merge_with(f, xs, ys):
    xs = a_copy_of(xs) # dict(xs), maybe generalizable?
    for (y, v) in ys.iteritems():
        xs[y] = v if y not in xs else f(xs[x], v)

merge_with((lambda x, y: x + y), A, B)

vous pourriez facilement généraliser ceci:

def merge_dicts(f, *dicts):
    result = {}
    for d in dicts:
        for (k, v) in d.iteritems():
            result[k] = v if k not in result else f(result[k], v)

, Alors il peut prendre n'importe quel nombre de dicts.

2
répondu Jonas Kölker 2016-06-10 13:11:05

C'est une solution simple pour fusionner deux dictionnaires où += peut être appliqué aux valeurs, il doit itérer sur un dictionnaire une seule fois, je suis surpris que personne n'a suggéré cette

a = {'a':1, 'b':2, 'c':3}

dicts = [{'b':3, 'c':4, 'd':5},
         {'c':9, 'a':9, 'd':9}]

def merge_dicts(merged,mergedfrom):
    for k,v in mergedfrom.items():
        if k in merged:
            merged[k] += v
        else:
            merged[k] = v
    return merged

for dct in dicts:
    a = merge_dicts(a,dct)
print (a)
#{'c': 16, 'b': 5, 'd': 14, 'a': 10}
2
répondu citizen2077 2017-06-29 09:01:56

fusion de trois dicts a, b, c en une seule ligne sans autres modules ou libs

si nous avons les trois dicts

a = {"a":9}
b = {"b":7}
c = {'b': 2, 'd': 90}

fusionner avec une seule ligne et retourner un objet dict en utilisant

c = dict(a.items() + b.items() + c.items())

Retour "à la 151940920"

{'a': 9, 'b': 2, 'd': 90}
2
répondu user6830669 2017-11-17 09:18:43

Cette solution est facile à utiliser, il est utilisé comme un dictionnaire, mais vous pouvez utiliser la fonction somme.

class SumDict(dict):
    def __add__(self, y):
        return {x: self.get(x, 0) + y.get(x, 0) for x in set(self).union(y)}

A = SumDict({'a': 1, 'c': 2})
B = SumDict({'b': 3, 'c': 4})  # Also works: B = {'b': 3, 'c': 4}
print(A + B)  # OUTPUT {'a': 1, 'b': 3, 'c': 6}
1
répondu Ignacio Villela 2016-12-09 14:26:10

les solutions ci-dessus sont grandes pour le scénario où vous avez un petit nombre de Counter s. Si vous avez une grande liste d'entre eux cependant, quelque chose comme ceci est beaucoup plus agréable:

from collections import Counter

A = Counter({'a':1, 'b':2, 'c':3})
B = Counter({'b':3, 'c':4, 'd':5}) 
C = Counter({'a': 5, 'e':3})
list_of_counts = [A, B, C]

total = sum(list_of_counts, Counter())

print(total)
# Counter({'c': 7, 'a': 6, 'b': 5, 'd': 5, 'e': 3})

la solution ci-dessus consiste essentiellement à additionner le Counter s par:

total = Counter()
for count in list_of_counts:
    total += count
print(total)
# Counter({'c': 7, 'a': 6, 'b': 5, 'd': 5, 'e': 3})

Cela fait la même chose, mais je pense qu'il est toujours utile de voir ce qu'il fait effectivement en dessous.

0
répondu Michael Hall 2017-08-02 07:19:34

Ce sujet:

def dict_merge_and_sum( d1, d2 ):
    ret = d1
    ret.update({ k:v + d2[k] for k,v in d1.items() if k in d2 })
    ret.update({ k:v for k,v in d2.items() if k not in d1 })
    return ret

A = {'a': 1, 'b': 2, 'c': 3}
B = {'b': 3, 'c': 4, 'd': 5}

print( dict_merge_and_sum( A, B ) )

sortie:

{'d': 5, 'a': 1, 'c': 7, 'b': 5}
0
répondu Lacobus 2018-06-12 17:45:22

le meilleur à utiliser est dict ():

A = {'a':1, 'b':2, 'c':3}
B = {'b':3, 'c':4, 'd':5}
Merged = dict(A, **B)
Merged == {'a':1, 'b':3, 'c':3, 'd':5}
-2
répondu tsabi 2017-01-18 17:14:40