Подтвердить что ты не робот

Есть ли какой-нибудь pythonic способ объединить два dicts (добавление значений для ключей, которые появляются в обоих)?

Например, у меня два диктата:

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

Мне нужен питонический способ "комбинирования" двух диктов, чтобы результат:

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

То есть: если в обоих dicts появляется ключ, добавьте их значения, если они появляются только в одном dict, сохраните его значение.

4b9b3361

Ответ 1

Используйте 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})

Счётчики в основном являются подклассом dict, поэтому вы можете делать все остальное с ними, которые вы обычно делаете с этим типом, например, перебирать их ключи и значения.

Ответ 2

Более общее решение, которое работает и для нечисловых значений:

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

или даже более общий:

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

Например:

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

Ответ 3

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

Ответ 4

Введение: Есть (возможно) лучшие решения. Но вы должны знать это и помнить об этом, и иногда вам нужно надеяться, что ваша версия Python не будет слишком старой или какой бы то ни было проблемой.

Тогда есть самые "хакерские" решения. Они великие и короткие, но иногда их трудно понять, читать и запоминать.

Существует, однако, альтернатива, которая заключается в попытке изобретать колесо. - Зачем изобретать колесо? - Вообще-то, потому что это действительно хороший способ учиться (а иногда только потому, что уже существующий инструмент не делает именно то, что вы хотели бы и/или как вам это понравится) и самый простой способ, если вы не знаете или не помните идеальный инструмент для своей проблемы.

Итак, я предлагаю изобрести колесо класса Counter из модуля collections (частично, по крайней мере):

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}

Возможно, есть другие способы реализовать это, и уже есть инструменты для этого, но всегда приятно визуализировать, как в основном будут работать.

Ответ 5

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

Ответ 6

Тот, у кого нет дополнительных импортных ресурсов!

Их пифонский стандарт называется EAFP (проще просить прощения, чем разрешения). Ниже код основан на этом стандарте 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: благодаря jerzyk для его предложений по улучшению.

Ответ 7

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}

ИЛИ ЖЕ

В качестве альтернативы вы можете использовать Counter, как упомянул выше @Martijn.

Ответ 8

Определенное суммирование Counter() является самым питоническим способом в таких случаях, но , только если оно приводит к положительному значению. Вот пример и, как вы можете видеть, нет результата c в результате после отрицания значения c в словаре 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})

Это потому, что Counter в основном предназначалось для работы с положительными целыми числами для представления числа запусков (отрицательный счет бессмыслен). Но чтобы помочь с этими вариантами использования, python документирует минимальные ограничения диапазона и типа следующим образом:

  • Сам класс Counter - это словарь подкласса без ограничений на его ключи и значения. Значения предназначенные для чисел, представляющих подсчеты, но вы можете хранить ничего в поле значений.
  • Метод most_common() требует только что значения могут быть упорядоченными.
  • Для операций на месте, таких как c[key] += 1, тип значения должен поддерживать только сложение и вычитание. Таким образом, фракции, поплавки и десятичные знаки будут работать, а отрицательные значения поддерживается. То же самое справедливо и для update() и subtract()разрешить отрицательные и нулевые значения для обоих входов и выходов.
  • Многопользовательские методы предназначены только для использования с положительными значениями. Входы могут быть отрицательными или нулевыми, но только выходы с положительным значения создаются. Нет ограничений типа, но тип значения необходимо поддерживать сложение, вычитание и сравнение.
  • Метод elements() требует целочисленных подсчетов. Он игнорирует нулевые и отрицательные значения.

Итак, чтобы обойти эту проблему после суммирования счетчика, вы можете использовать Counter.update, чтобы получить выход желания. Он работает как dict.update(), но добавляет количество вместо вместо них.

In [24]: A.update(B)

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

Ответ 9

Для более общего и расширяемого способа отметьте mergedict. Он использует singledispatch и может объединять значения на основе своих типов.

Пример:

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

Ответ 10

Из python 3.5: слияние и суммирование

Благодаря @tokeinizer_fsj, который сказал мне в комментарии, что я полностью не понял смысл вопроса (я думал, что add означает просто добавление ключей, которые в конечном итоге различаются в двух диктаторах, а вместо этого я имел в виду, что общие значения ключей следует суммировать). Поэтому я добавил этот цикл перед слиянием, так что второй словарь содержит сумму общих ключей. Последний словарь будет тем, чьи значения будут длиться в новом словаре, что является результатом слияния двух, поэтому я решил, что проблема решена. Решение действительно из python 3.5 и следующих версий.

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}

Многоразовый код

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

Ответ 11

Кроме того, обратите внимание, что a.update( b ) составляет 2x быстрее, чем 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

Ответ 12

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)

Вы можете легко обобщить это:

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)

Тогда он может принимать любое число dicts.

Ответ 13

Это простое решение для слияния двух словарей, где += можно применить к значениям, он должен перебирать словарь только один раз, я удивлен, что никто не предложил этот

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}

Ответ 14

Слияние трех dicts a, b, c в одной строке без каких-либо других модулей или libs

Если у нас есть три dicts

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

Объединить все с одной строкой и вернуть объект dict с помощью

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

Возврат

{'a': 9, 'b': 2, 'd': 90}

Ответ 15

Это решение прост в использовании, оно используется как обычный словарь, но вы можете использовать функцию sum.

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}

Ответ 16

Вышеупомянутые решения отлично подходят для сценария, где у вас небольшое количество Counter s. Если у вас есть большой список из них, что-то вроде этого гораздо приятнее:

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

Вышеупомянутое решение по существу суммирует Counter по:

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

Это делает то же самое, но я думаю, что он всегда помогает понять, что он эффективно делает под ним.

Ответ 17

Как насчет:

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

Вывод:

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

Ответ 18

Лучше всего использовать 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}