Question Existe-t-il une manière pythonique de combiner deux dictons (en ajoutant des valeurs pour les touches 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'une façon pythonique de «combiner» deux dictionnaires de sorte que le résultat soit:

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

C'est-à-dire: si une clé apparaît dans les deux dictons, ajoutez leurs valeurs, si elle apparaît dans un seul dict, conservez sa valeur.


418
2018-06-13 09:17


origine


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, vous pouvez toujours faire tout ce que vous feriez normalement avec ce type, comme itérer leurs clés et leurs valeurs.


772
2018-06-13 09:22



Une solution plus générique, qui fonctionne également 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 même 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}

108
2018-06-13 09:41



>>> 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}

59
2018-06-13 09:25



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

Ensuite, il y a les solutions les plus «piratées». Ils sont grands et courts mais sont parfois difficiles à comprendre, à lire et à mémoriser.

Il existe cependant une alternative qui consiste à essayer de réinventer la roue. - Pourquoi réinventer la roue? - Généralement parce que c'est un très bon moyen d'apprendre (et parfois juste parce que l'outil déjà existant ne fait pas exactement ce que vous voulez et / ou comme vous le voudriez) et le moyen le plus simple si vous ne savez pas ou ne vous souvenez pas de l'outil parfait pour votre problème.

Alors, Je propose de réinventer la roue du Counter classe de la collections module (partiellement 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 moyens d'implémenter cela et il existe déjà des outils pour le faire, mais il est toujours agréable de visualiser comment les choses fonctionneraient.


43
2018-02-18 06:45



myDict = {}
for k in itertools.chain(A.keys(), B.keys()):
    myDict[k] = A.get(k, 0)+B.get(k, 0)

11
2017-07-04 10:06



Celui avec pas d'importations supplémentaires!

Leur est un norme pythonique appelé EAFP(Plus facile de demander le pardon que la permission). Le code ci-dessous est basé sur cela norme 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: grâce à Jerzyk pour ses suggestions d'amélioration.


11
2018-05-03 06:20



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 Counter comme @Martijn a mentionné ci-dessus.


8
2017-07-04 10:49



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

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 Counters ont été principalement conçus pour fonctionner avec des nombres entiers positifs pour représenter les comptes courants (le nombre négatif n'a pas de sens). Mais pour aider avec ces cas d'utilisation, python documente les restrictions de portée et de type minimum comme suit:

  • La classe Counter elle-même est un dictionnaire   sous-classe sans restrictions sur ses clés et valeurs. Les valeurs sont   destiné à être des nombres représentant des chiffres, mais vous pouvez stocker   quoi que ce soit dans le champ de valeur.
  • le most_common() la méthode ne nécessite que   que les valeurs soient ordonnables.
  • Pour les opérations sur place telles que c[key] += 1, le type de valeur ne nécessite que l'addition et la soustraction. Donc les fractions, les flottants et les décimales fonctionneraient et les valeurs négatives   prise en charge. La même chose est également vraie pour update()et subtract() lequel   autoriser les valeurs négatives et nulles 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 uniquement les sorties positives   les valeurs sont créées. Il n'y a pas de restrictions de type, mais le type de valeur   doit prendre en charge l'addition, la soustraction et la comparaison.
  • le elements() méthode nécessite des nombres entiers. Il ignore les comptes nuls et négatifs.

Donc, pour contourner ce problème après avoir additionné votre compteur, vous pouvez utiliser Counter.update afin d'obtenir la sortie désirée. Cela fonctionne comme dict.update() mais ajoute des comptes au lieu de les remplacer.

In [24]: A.update(B)

In [25]: A
Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1})

8
2018-03-10 11:12



Pour une vérification plus générique et extensible fusion. Il utilise singledispatch et peut fusionner des valeurs en fonction de 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
2017-11-14 10:43



En outre, s'il vous plaît noter a.update( b ) est 2x 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
2017-08-20 15:11



De python 3.5: fusion et sommation

Merci à @tokeinizer_fsj qui m'a dit dans un commentaire que je n'obtenais pas complètement le sens de la question (je pensais que ajouter signifiait simplement ajouter des clés qui étaient finalement différentes dans les deux dictatures et, à la place, je voulais dire que les valeurs clés communes devrait être additionné). J'ai donc ajouté cette boucle avant la fusion, de sorte que le deuxième dictionnaire contienne la somme des clés communes. Le dernier dictionnaire sera celui dont les valeurs vont durer 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
2017-08-28 09:47