Есть ли питоний способ объединить два словаря (сложив значения для ключей, которые присутствуют в обоих)?
Задача заключается в следующем: у меня есть два словаря:
Dict A: {'a': 1, 'b': 2, 'c': 3}
Dict B: {'b': 3, 'c': 4, 'd': 5}
Мне нужно "объединить" эти два словаря таким образом, чтобы результатом был следующий словарь:
{'a': 1, 'b': 5, 'c': 7, 'd': 5}
То есть, если ключ присутствует в обоих словарях, необходимо сложить их значения. Если же ключ есть только в одном из словарей, нужно сохранить его значение.
Как я могу реализовать это на Python, используя при этом "питонистичный" подход?
5 ответ(ов)
Для более общего решения, которое работает с нечисловыми значениями, вы можете использовать следующий код:
a = {'a': 'foo', 'b': 'bar', 'c': 'baz'}
b = {'a': 'spam', 'c': 'ham', 'x': 'blah'}
r = dict(list(a.items()) + list(b.items()) +
[(k, a[k] + b[k]) for k in set(b) & set(a)])
Либо еще более универсальное решение:
import operator
def combine_dicts(a, b, op=operator.add):
return dict(list(a.items()) + list(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}
>>> print(combine_dicts(a, b, operator.mul))
{'a': 10, 'x': 7, 'c': 24, 'b': 3}
Вы получите результат, где значения, совпадающие по ключам в обоих словарях, будут объединены с помощью заданой операции (в данном случае умножение). Таким образом, решение универсально и может применяться к различным типам данных, не ограничиваясь только числами.
Вы можете объединить два словаря A
и B
и суммировать значения по общим ключам следующим образом:
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}
В этом коде мы создаем новый словарь c
, где для каждого ключа x
, который находится в объединении ключей из обоих словарей, мы используем метод get()
для получения значений. Если ключа нет в одном из словарей, будет возвращено значение по умолчанию, равное 0
. Таким образом, мы суммируем значения по общим ключам и добавляем ключи, которые есть только в одном из словарей.
Введение: Существует множество (возможно, лучших) решений различных задач. Но нужно знать их и помнить, и иногда надеяться, что ваша версия Python не слишком старая или что-то в этом роде.
Затем есть более "хакерские" решения. Они отличные и короткие, но иногда трудны для понимания, чтения и запоминания.
Тем не менее, есть альтернатива: попробовать reinvent the wheel (изобрести колесо заново).
- Почему стоит это делать?
- В общем, потому что это действительно хороший способ учиться (иногда просто потому, что уже существующий инструмент делает не совсем то, что вам нужно и/или не так, как вам бы хотелось) и самый простой путь, если вы не знаете или не помните идеальный инструмент для вашей задачи.
Итак, я предлагаю reinvent the wheel для класса 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 # Здесь можно изменить логику
else:
r[key] = val
except AttributeError: # На случай, если oth не является словарем
return NotImplemented # Конвенция для необработанных случаев
return r
a = MyDict({'a': 1, 'b': 2, 'c': 3})
b = MyDict({'b': 3, 'c': 4, 'd': 5})
print(a + b) # Вывод: {'a': 1, 'b': 5, 'c': 7, 'd': 5}
Существуют, вероятно, и другие способы реализации этого, уже есть инструменты, которые это делают, но всегда приятно визуализировать, как все это будет работать в своем базовом виде.
Определенно, складывать объекты Counter()
— это самый "питоничный" способ в подобных случаях, но только если это приводит к положительному значению. Вот пример: как вы можете видеть, после отрицания значения c
в словаре B
в результате отсутствует c
.
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, вы можете использовать Counter.update
, чтобы получить желаемый результат. Это работает как dict.update()
, но добавляет количества, а не заменяет их.
In [24]: A.update(B)
In [25]: A
Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1})
Ваш код создает новый словарь myDict
, который суммирует значения из двух других словарей A
и B
по ключам.
Вот краткое объяснение вашего кода:
- Вы создаете пустой словарь
myDict
. - Используя
itertools.chain
, вы объединяете ключи из обоих словарейA
иB
. - В цикле вы проходите по всем ключам и добавляете их в
myDict
. Для каждого ключаk
вы используете методget
, чтобы получить значения изA
иB
. Если ключа нет в каком-либо из словарей, будет возвращено значение0
(это позволяет избежать ошибок при обращении к несуществующим ключам). - Таким образом, итоговое значение для ключа
k
вmyDict
будет равно сумме значений из словарейA
иB
.
Если вам требуется другое объяснение или уточнение, пожалуйста, дайте знать!
Преобразование списка словарей в DataFrame pandas
Почему использовать dict.get(key) вместо dict[key]?
Как преобразовать вложенный словарь Python в объект?
Ошибка: "'dict' объект не имеет метода 'iteritems'"
Добавить новый элемент в словарь в Python