it-swarm.com.de

Gibt es eine Pythonic-Methode, um zwei Diktate zu kombinieren (Werte für Schlüssel hinzuzufügen, die in beiden angezeigt werden)?

Zum Beispiel habe ich zwei Diktate:

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

Ich brauche eine pythonische Methode, um zwei Diktate so zu "kombinieren", dass das Ergebnis lautet:

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

Das heißt: Wenn ein Schlüssel in beiden Diktaten erscheint, fügen Sie deren Werte hinzu. Wenn er nur in einem Diktat erscheint, behalten Sie seinen Wert.

440
Spirit Zhang

Verwenden Sie 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})

Zähler sind im Grunde eine Unterklasse von dict. Sie können also alle anderen Funktionen ausführen, die Sie normalerweise mit diesem Typ machen würden, z.

796
Martijn Pieters

Eine generischere Lösung, die auch für nicht numerische Werte funktioniert:

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)])

oder noch allgemeiner:

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)])

Zum Beispiel:

>>> 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}
112
georg
>>> 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}
63

Intro: Es gibt die (wahrscheinlich) besten Lösungen. Aber Sie müssen es wissen und sich daran erinnern, und manchmal müssen Sie hoffen, dass Ihre Python-Version nicht zu alt ist oder was auch immer das Problem sein könnte.

Dann gibt es die hackigsten Lösungen. Sie sind großartig und kurz, aber manchmal schwer zu verstehen, zu lesen und sich zu erinnern.

Es gibt jedoch eine Alternative, bei der versucht wird, das Rad neu zu erfinden. Warum das Rad neu erfunden? Im Allgemeinen, weil dies ein wirklich guter Weg zum Lernen ist (und manchmal nur, weil das bereits vorhandene Tool dies nicht tut tun Sie nicht genau das, was Sie möchten und/oder wie Sie es möchten) und den einfachsten Weg, wenn Sie nicht das perfekte Werkzeug für Ihr Problem kennen oder sich daran erinnern.

Also , ich schlage vor, das Rad der Counter-Klasse aus dem collections-Modul (zumindest teilweise) neu zu erfinden:

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}

Es gibt wahrscheinlich andere Möglichkeiten, dies zu implementieren, und es gibt bereits Werkzeuge, um dies zu tun, aber es ist immer schön, sich vorzustellen, wie die Dinge im Wesentlichen funktionieren würden.

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

Die mit keine zusätzlichen Importe!

Ihr ist ein Pythonic Standard genannt EAFP (Leichter um Vergebung zu bitten als Erlaubnis). Der folgende Code basiert auf diesem Python-Standard.

# 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: danke an jerzyk für seine Verbesserungsvorschläge.

12
Devesh Saini

Das Aufsummieren der Counter()s ist definitiv der Pythonic-Weg in solchen Fällen, aber nur wenn dies zu einem positiven Wert führt . Hier ist ein Beispiel, und wie Sie sehen, gibt es keine c im Ergebnis, nachdem der Wert der c im B-Wörterbuch negiert wurde.

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

Das liegt daran, dass Counters in erster Linie dafür entwickelt wurden, mit positiven Ganzzahlen zu arbeiten, um laufende Zählwerte darzustellen (negative Zählweise ist bedeutungslos). Um jedoch bei diesen Anwendungsfällen zu helfen, dokumentiert Python die Mindestbereichs- und Typeinschränkungen wie folgt:

  • Die Counter-Klasse selbst ist ein Wörterbuch Unterklasse ohne Einschränkungen für ihre Schlüssel und Werte. Die Werte sind Sie sind Zahlen, die Zahlen darstellen, aber Sie können .__ speichern. alles im Wertefeld.
  • Die most_common()-Methode erfordert nur dass die Werte bestellbar sind.
  • Für In-Place-Operationen wie c[key]+= 1, der Werttyp muss nur die Addition und Subtraktion unterstützen. Brüche, Floats und Dezimalzahlen würden also funktionieren und negative Werte sind unterstützt. Dasselbe gilt auch für update() und subtract() welche Erlauben Sie negative Werte und Nullwerte für Ein- und Ausgänge.
  • Die Multiset-Methoden sind nur für Anwendungsfälle mit positiven Werten konzipiert. Die Eingänge können negativ oder null sein, aber nur Ausgänge mit positivem Werte werden erstellt. Es gibt keine Typeinschränkungen, sondern den Werttyp muss Addition, Subtraktion und Vergleich unterstützen.
  • Die elements()-Methode erfordert ganzzahlige Zählungen. Null und negative Zählungen werden ignoriert.

Um dieses Problem zu umgehen, nachdem Sie Ihren Counter summiert haben, können Sie Counter.update verwenden, um die gewünschte Ausgabe zu erhalten. Es funktioniert wie dict.update(), fügt jedoch Zählungen hinzu, anstatt sie zu ersetzen.

In [24]: A.update(B)

In [25]: A
Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1})
10
Kasrâmvd
import itertools
import collections

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

new_dict = collections.defaultdict(int)
# use dict.items() instead of dict.iteritems() for Python3
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}

ODER 

Alternativ können Sie Counter verwenden, da @Martijn oben erwähnt wurde.

10
Adeel

Für einen allgemeineren und erweiterbaren Weg überprüfen Sie mergedict . Es verwendet singledispatch und kann Werte basierend auf seinen Typen zusammenführen.

Beispiel:

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

Beachten Sie außerdem, dass a.update( b ) zweimal schneller als a + b ist.

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
4
shouldsee

Ab Python 3.5: Zusammenführen und Summieren

Danke an @tokeinizer_fsj, der mir in einem Kommentar sagte, dass ich die Bedeutung der Frage nicht vollständig verstanden habe (ich dachte, Addieren bedeutete, dass man nur Schlüssel hinzufügte, die sich in den beiden Diktierarten letztendlich unterschieden, und stattdessen die gemeinsamen Schlüsselwerte sollte summiert werden). Also fügte ich diese Schleife vor dem Zusammenführen hinzu, so dass das zweite Wörterbuch die Summe der allgemeinen Schlüssel enthält. Das letzte Wörterbuch wird dasjenige sein, dessen Werte im neuen Wörterbuch als Ergebnis der Verschmelzung der beiden Werte erhalten bleiben. Das Problem ist also gelöst. Die Lösung ist ab Python 3.5 und den folgenden Versionen gültig.

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}

Wiederverwendbarer Code

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
Giovanni Gianni

Dies ist eine einfache Lösung zum Zusammenführen von zwei Wörterbüchern, bei denen += auf die Werte angewendet werden kann. Es muss nur einmal ein Wörterbuch durchlaufen werden. Ich bin überrascht, dass dies niemand vorgeschlagen hat

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
citizen2077

Zusammenführen von drei Diagrammen a, b, c in einer einzigen Zeile ohne andere Module oder Bibliotheken

Wenn wir die drei Wörter haben

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

Alle mit einer einzigen Zeile zusammenführen und ein Diktierobjekt mit zurückgeben

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

Rückkehr

{'a': 9, 'b': 2, 'd': 90}
2
user6830669
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)

Sie könnten dies leicht verallgemeinern:

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)

Dann kann es eine beliebige Anzahl von Diktaten dauern.

2
Jonas Kölker

Diese Lösung ist einfach zu verwenden und wird als normales Wörterbuch verwendet. Sie können jedoch die Summenfunktion verwenden.

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
Ignacio Villela

Die oben genannten Lösungen eignen sich hervorragend für das Szenario, in dem Sie eine kleine Anzahl von Counters haben. Wenn Sie jedoch eine große Liste von ihnen haben, ist so etwas viel schöner:

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

Die obige Lösung summiert im Wesentlichen die Counters durch:

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

Das tut das Gleiche, aber ich denke, es hilft immer, zu sehen, was es effektiv macht.

0
Michael Hall

Wie wäre es mit:

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

Ausgabe:

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