Вопрос по python – Слияние и сумма двух словарей

37

У меня есть словарь ниже, и я хочу добавить в другой словарь необязательные элементы и объединить его результаты. Есть ли встроенная функция для этого, или мне нужно будет сделать свою собственную?

<code>{
  '6d6e7bf221ae24e07ab90bba4452267b05db7824cd3fd1ea94b2c9a8': 6,
  '7c4a462a6ed4a3070b6d78d97c90ac230330603d24a58cafa79caf42': 7,
  '9c37bdc9f4750dd7ee2b558d6c06400c921f4d74aabd02ed5b4ddb38': 9,
  'd3abb28d5776aef6b728920b5d7ff86fa3a71521a06538d2ad59375a': 15,
  '2ca9e1f9cbcd76a5ce1772f9b59995fd32cbcffa8a3b01b5c9c8afc2': 11
}
</code>

Количество элементов в словаре также неизвестно.

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

Вы можете захотетьedit Ваш вопрос и предоставить лучшую (и правильную) информацию, иначе этот вопрос, скорее всего, будет закрыт. Rik Poggi
Пожалуйста, уточните свою терминологию; это диктат, а не список. Кроме того, какой результат вы ожидаете, и что вы пытались? Fred Foo

Ваш Ответ

9   ответов
0

dict как| использовать:

>>> dict({'a': 1,'c': 2}, **{'c': 1})
{'a': 1, 'c': 1}
1

dict& APOS; supdate метод:

>>> d1 = {1:2,3:4}
>>> d2 = {5:6,7:8}
>>> d1.update(d2)
>>> d1
{1: 2, 3: 4, 5: 6, 7: 8}
Error: User Rate Limit Exceeded badc0re
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded badc0re
Error: User Rate Limit Exceeded badc0re
Error: User Rate Limit ExceededdictError: User Rate Limit Exceeded[(1,2),(3,4)]Error: User Rate Limit Exceeded
10

Georg, NPE а такжеСкотт.

Я пытался выполнить это действие на коллекциях 2or more словари и было интересно посмотреть, сколько времени потребовалось для каждого. Поскольку я хотел сделать это в любом количестве словарей, мне пришлось немного изменить некоторые ответы. Если у кого-то есть лучшие предложения для них, не стесняйтесь редактировать.

Вот мой метод испытаний. Я недавно обновил его, чтобы включить тесты с НАМНОГО более крупными словарями:

Сначала я использовал следующие данные:

import random

x = {'xy1': 1, 'xy2': 2, 'xyz': 3, 'only_x': 100}
y = {'xy1': 10, 'xy2': 20, 'xyz': 30, 'only_y': 200}
z = {'xyz': 300, 'only_z': 300}

small_tests = [x, y, z]

# 200,000 random 8 letter keys
keys = [''.join(random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(8)) for _ in range(200000)]

a, b, c = {}, {}, {}

# 50/50 chance of a value being assigned to each dictionary, some keys will be missed but meh
for key in keys:
    if random.getrandbits(1):
        a[key] = random.randint(0, 1000)
    if random.getrandbits(1):
        b[key] = random.randint(0, 1000)
    if random.getrandbits(1):
        c[key] = random.randint(0, 1000)

large_tests = [a, b, c]

print("a:", len(a), "b:", len(b), "c:", len(c))
#: a: 100069 b: 100385 c: 99989

Теперь каждый из методов:

from collections import defaultdict, Counter

def georg_method(tests):
    return {k: sum(t.get(k, 0) for t in tests) for k in set.union(*[set(t) for t in tests])}

def georg_method_nosum(tests):
    # If you know you will have exactly 3 dicts
    return {k: tests[0].get(k, 0) + tests[1].get(k, 0) + tests[2].get(k, 0) for k in set.union(*[set(t) for t in tests])}

def npe_method(tests):
    ret = defaultdict(int)
    for d in tests:
        for k, v in d.items():
            ret[k] += v
    return dict(ret)

# Note: There is a bug with scott's method. See below for details.
def scott_method(tests):
    return dict(sum((Counter(t) for t in tests), Counter()))

def scott_method_nosum(tests):
    # If you know you will have exactly 3 dicts
    return dict(Counter(tests[0]) + Counter(tests[1]) + Counter(tests[2]))

methods = {"georg_method": georg_method, "georg_method_nosum": georg_method_nosum,
           "npe_method": npe_method,
           "scott_method": scott_method, "scott_method_nosum": scott_method_nosum}

Я также написал быструю функцию поиска различий между списками. К сожалению, именно тогда я обнаружил проблему в методе Скотта, а именно, если у вас есть словари, которые составляют 0, словарь вообще не будет включен из-за того, какCounter() ведет себя при добавлении.

Наконец, результаты:

Results: Small Tests
for name, method in methods.items():
    print("Method:", name)
    %timeit -n10000 method(small_tests)
#: Method: npe_method
#: 10000 loops, best of 3: 5.16 µs per loop
#: Method: georg_method_nosum
#: 10000 loops, best of 3: 8.11 µs per loop
#: Method: georg_method
#: 10000 loops, best of 3: 11.8 µs per loop
#: Method: scott_method_nosum
#: 10000 loops, best of 3: 42.4 µs per loop
#: Method: scott_method
#: 10000 loops, best of 3: 65.3 µs per loop
Results: Large Tests

Естественно, не может работать где-либо рядом с таким количеством петель

for name, method in methods.items():
    print("Method:", name)
    %timeit -n10 method(large_tests)
#: Method: npe_method
#: 10 loops, best of 3: 227 ms per loop
#: Method: georg_method_nosum
#: 10 loops, best of 3: 327 ms per loop
#: Method: georg_method
#: 10 loops, best of 3: 455 ms per loop
#: Method: scott_method_nosum
#: 10 loops, best of 3: 510 ms per loop
#: Method: scott_method
#: 10 loops, best of 3: 600 ms per loop
Conclusion
╔═══════════════════════════╦═══════╦═════════════════════════════╗
║                           ║       ║   Best of 3 Time Per Loop   ,║
║         Algorithm         ║  By   ╠══════════════╦══════════════╣
║                           ║       ║  small_tests ║  large_tests ║
╠═══════════════════════════╬═══════╬══════════════╬══════════════╣
║ defaultdict sum           ║ NPE   ║      5.16 µs ║   227,000 µs ║
║ set unions without sum()  ║ georg ║      8.11 µs ║   327,000 µs ║
║ set unions with sum()     ║       ║      11.8 µ,;s ║   455,000 µs ║
║ Counter() without sum()   ║ Scott ║      42.4 µs ║   510,000 µs ║
║ Counter() with sum()      ║       ║      65.3 µs ║   600,000 µs ║
╚═══════════════════════════╩═══════╩══════════════╩══════════════╝

Важный. YMMV.

1
class dict_merge(dict):
def __add__(self, other):
    result = dict_merge({})
    for key in self.keys():
        if key in other.keys():
            result[key] = self[key] + other[key]
        else:
            result[key] = self[key]
    for key in other.keys():
        if key in self.keys():
            pass
        else:
            result[key] = other[key]
    return result


a = dict_merge({"a":2, "b":3, "d":4})
b = dict_merge({"a":1, "b":2})
c = dict_merge({"a":5, "b":6, "c":5})
d = dict_merge({"a":8, "b":6, "e":5})

print((a + b + c +d))


>>> {'a': 16, 'b': 17, 'd': 4, 'c': 5, 'e': 5}

Это перегрузка оператора. С помощью__add__мы определили, как использовать оператор+ за нашихdict_merge который наследует от встроенного питонаdict, Вы можете пойти дальше и сделать его более гибким, используя аналогичный способ определения других операторов в этом же классе, например* с__mul__ для умножения, или/ с__div__ для деления, или даже% с__mod__ по модулю и замене+ вself[key] + other[key] с соответствующим оператором, если вам когда-нибудь понадобится такое объединение. Я только что проверил это, как и без других операторов, но я не предвижу проблему с другими операторами. Просто учись, пытаясь.

25

+, -, &, а также| (пересечение и объединение) наcollections.Counter().

Таким образом, мы можем сделать следующее (Примечание: в словаре останутся только положительные значения):

from collections import Counter

x = {'both1':1, 'both2':2, 'only_x': 100 }
y = {'both1':10, 'both2': 20, 'only_y':200 }

z = dict(Counter(x)+Counter(y))

print(z) # {'both2': 22, 'only_x': 100, 'both1': 11, 'only_y': 200}

Для адресации добавления значений, где результат может быть нулевым или отрицательным, используйтеCounter.update() для добавления иCounter.subtract() для вычитания:

x = {'both1':0, 'both2':2, 'only_x': 100 }
y = {'both1':0, 'both2': -20, 'only_y':200 }
xx = Counter(x)
yy = Counter(y)
xx.update(yy)
dict(xx) # {'both2': -18, 'only_x': 100, 'both1': 0, 'only_y': 200}
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded'both1': 0Error: User Rate Limit ExceededxError: User Rate Limit ExceededyError: User Rate Limit Exceeded'both1': 0Error: User Rate Limit ExceededzError: User Rate Limit Exceeded'both1'Error: User Rate Limit Exceededz.
3

единять произвольную коллекцию словарей:

from functools import reduce

collection = [
    {'a': 1, 'b': 1},
    {'a': 2, 'b': 2},
    {'a': 3, 'b': 3},
    {'a': 4, 'b': 4, 'c': 1},
    {'a': 5, 'b': 5, 'c': 1},
    {'a': 6, 'b': 6, 'c': 1},
    {'a': 7, 'b': 7},
    {'a': 8, 'b': 8},
    {'a': 9, 'b': 9},
]


def reducer(accumulator, element):
    for key, value in element.items():
        accumulator[key] = accumulator.get(key, 0) + value
    return accumulator


total = reduce(reducer, collection, {})


assert total['a'] == sum(d.get('a', 0) for d in collection)
assert total['b'] == sum(d.get('b', 0) for d in collection)
assert total['c'] == sum(d.get('c', 0) for d in collection)

print(total)

Исполнение:

{'a': 45, 'b': 45, 'c': 3}

Преимущества:

Simple, clear, Pythonic. Schema-less, as long all keys are "sumable". O(n) temporal complexity and O(1) memory complexity.
1
d1 = {'apples': 2, 'banana': 1}
d2 = {'apples': 3, 'banana': 2}
merged = reduce(
    lambda d, i: (
        d.update(((i[0], d.get(i[0], 0) + i[1]),)) or d
    ),
    d2.iteritems(),
    d1.copy(),
)

Есть также довольно простая заменаdict.update():

merged = dict(d1, **d2)
Error: User Rate Limit Exceededmerged = dict(d1, **d2)
111

как именно вы хотите объединиться, поэтому сделайте свой выбор:

x = {'both1':1, 'both2':2, 'only_x': 100 }
y = {'both1':10, 'both2': 20, 'only_y':200 }

print { k: x.get(k, 0) + y.get(k, 0) for k in set(x) }
print { k: x.get(k, 0) + y.get(k, 0) for k in set(x) & set(y) }
print { k: x.get(k, 0) + y.get(k, 0) for k in set(x) | set(y) }

Результаты:

{'both2': 22, 'only_x': 100, 'both1': 11}
{'both2': 22, 'both1': 11}
{'only_y': 200, 'both2': 22, 'both1': 11, 'only_x': 100}
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededx-y. diff= { k: x.get(k, 0) - y.get(k, 0) for k in set(x) | set(y) } print(diff)Error: User Rate Limit Exceeded{'only_y': -200, 'both2': -18, 'only_x': 100, 'both1': -9}Error: User Rate Limit Exceededonly_yError: User Rate Limit Exceeded200Error: User Rate Limit Exceeded200Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededsub = lambda a, b: a if b is None else b if a is None else a -bError: User Rate Limit Exceeded{k: sub(x.get(k), y.get(k)) for ... etc
17

defaultdict за это:

from collections import defaultdict

def dsum(*dicts):
    ret = defaultdict(int)
    for d in dicts:
        for k, v in d.items():
            ret[k] += v
    return dict(ret)

x = {'both1':1, 'both2':2, 'only_x': 100 }
y = {'both1':10, 'both2': 20, 'only_y':200 }

print(dsum(x, y))

Это производит

{'both1': 11, 'both2': 22, 'only_x': 100, 'only_y': 200}

Похожие вопросы