두 개의 dicts (둘 다에 나타나는 키 값 추가)를 결합하는 파이썬적인 방법이 있습니까?


477

예를 들어 두 가지 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에만 표시되면 해당 값을 유지하십시오.

답변:


835

사용 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이므로 키와 값을 반복하는 것과 같이 일반적으로 해당 유형으로 수행하는 다른 모든 작업을 수행 할 수 있습니다.


4
이와 같이 병합 할 카운터가 여러 개 있습니까? sum(counters)불행히도 작동하지 않습니다.
Dr. Jan-Philip Gehrcke

27
@ Jan-PhilipGehrcke : sum()로 시작 값을 지정하십시오 sum(counters, Counter()).
Martijn Pieters

5
감사. 그러나이 방법은 합산 문자열이 맞기 때문에 중간 객체 생성의 영향을받습니다.
Dr. Jan-Philip Gehrcke

6
@ Jan-PhilipGehrcke : 다른 옵션은 루프를 사용하고 그 +=자리에서 합산을 수행하는 것입니다. res = counters[0]다음 for c in counters[1:]: res += c.
Martijn Pieters

3
나는 그 접근법을 좋아한다! 누군가가 사전을 처리하는 것에 가깝게 유지하는 것을 좋아한다면 : update()대신 사용할 수도 있습니다 . +=for c in counters[1:]: res.update(c)
Dr. Jan-Philip Gehrcke

119

숫자가 아닌 값에도 적용되는보다 일반적인 솔루션 :

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}

27
당신은 또한 사용할 수 있습니다 for k in b.viewkeys() & a.viewkeys()때, 파이썬 2.7을 사용 하고, 세트의 생성을 건너 뜁니다.
Martijn Pieters

set(a)튜플 세트가 아닌 키 세트를 반환합니까? 이것의 근거는 무엇입니까?
Sarsaparilla

1
@HaiPhan : kv 쌍이 아닌 키를 반복적으로 지시하기 때문입니다. CF list({..}), for k in {...}
게오르그

2
@Craicerjack : 예, operator.mul이 코드는 일반적이며 숫자 추가에만 국한되지 않음을 분명히했습니다.
georg

6
Python 3 호환 옵션을 추가 할 수 있습니까? {**a, **b, **{k: op(a[k], b[k]) for k in a.keys() & b}}파이썬 3.5 이상에서 작동합니다.
vaultah '11

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

1
for x in set(itertools.chain(A, B))더 논리적으로 사용하지 않습니까? dict에 set을 사용하면 키가 이미 독특하기 때문에 약간의 말이되지 않습니까? 나는 그것이 열쇠를 얻는 또 다른 방법이라는 것을 알고 있지만 그것을 사용 itertools.chain하는 것보다 더 혼란 스럽다는 것을 알았습니다 (당신이 무엇을 알고 있음을 암시합니다 itertools.chain)
jeromej

45

소개 : (아마도) 최상의 솔루션이 있습니다. 그러나 당신은 그것을 알고 그것을 기억해야하며 때로는 파이썬 버전이 너무 오래되었거나 문제가 될 수 있기를 바랍니다.

그런 다음 가장 '해킹 된'솔루션이 있습니다. 그것들은 위대하고 짧지 만 때로는 이해하고 읽고 기억하기가 어렵습니다.

그러나 바퀴를 재발 명하려는 대안이 있습니다. -왜 바퀴를 재발 명합니까? -일반적으로 학습하기에 좋은 방법이기 때문에 (때로는 이미 존재하는 도구가 원하는 것을 정확하게 수행하지 않거나 원하는 방식으로 수행하지 않기 때문에) 알고 있거나 모르는 경우 가장 쉬운 방법이기 때문입니다. 문제에 대한 완벽한 도구를 기억하지 마십시오.

그래서 나는 모듈 에서 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}

아마도 그것을 구현하는 다른 방법이있을 것입니다.이를 수행하는 도구가 이미 있지만 일이 기본적으로 어떻게 작동하는지 시각화하는 것이 좋습니다.


3
여전히 2.6 인 우리에게도
Brian B

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

13

추가 수입품없는 분!

그것들은 EAFP (권한보다 용서를 구하는 것이 더 쉽다) 라는 pythonic 표준 입니다. 아래 코드는 해당 파이썬 표준을 기반으로합니다 .

# 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

편집 : 그의 개선 제안에 대해 jerzyk 에게 감사드립니다 .


5
n ^ 2 알고리즘은 카운터 방법보다 상당히 느리다
Joop

@DeveshSaini가 더 좋지만 여전히 차선책이 아닙니다 :) 예 : 정말로 정렬이 필요합니까? 그렇다면 왜 두 개의 루프가 있습니까? 당신은 이미 newdict에 모든 키를 가지고 있습니다. 최적화를위한 작은 힌트
Jerzyk

이전 n ^ 2 알고리즘 대신 n ^ 1 알고리즘이 배치되었습니다. @Joop
Devesh Saini

11

확실히 Counter()s를 합산하는 것은 그러한 경우에 가장 파이썬적인 방법이지만 양의 값을 얻는 경우에만 가능합니다 . 다음은 예이며 사전 에서의 값을 c무시한 후 결과 가 없습니다 .cB

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

왜냐하면 Counters는 주로 양의 정수로 작동하여 연속 카운트를 나타내도록 설계 되었기 때문입니다 (음수는 의미가 없음). 그러나 이러한 사용 사례를 돕기 위해 Python은 최소 범위 및 유형 제한을 다음과 같이 문서화합니다.

  • Counter 클래스 자체는 키와 값에 제한이없는 사전 서브 클래스입니다. 값은 개수를 나타내는 숫자이지만 값 필드에 아무 것도 저장할 수 있습니다.
  • most_common()방법은 값을 주문할 수 있어야합니다.
  • 와 같은 내부 작업의 c[key] += 1경우 값 유형은 더하기 및 빼기 만 지원하면됩니다. 따라서 분수, 부동 소수점 및 소수가 작동하고 음수 값이 지원됩니다. update()and 도 마찬가지입니다.subtract() 이는 입력 및 출력 모두 마이너스 제로 값을 허용한다.
  • 다중 집합 방법은 양수 값을 가진 사용 사례에 대해서만 설계되었습니다. 입력은 음수이거나 0 일 수 있지만 양수 값의 출력 만 작성됩니다. 유형 제한은 없지만 값 유형은 더하기, 빼기 및 비교를 지원해야합니다.
  • elements()방법에는 정수 카운트가 필요합니다. 0과 음수를 무시합니다.

따라서 카운터를 합산 한 후 그 문제를 해결 Counter.update하기 위해 원하는 출력을 얻기 위해 사용할 수 있습니다 . 그것은 작동 dict.update()하지만 카운트 대신에 카운트를 추가합니다.

In [24]: A.update(B)

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

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

또는

@Martijn이 위에서 언급했듯이 Counter를 사용할 수 있습니다.


7

보다 일반적이고 확장 가능한 방법으로 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'}

5

Python 3.5에서 : 병합 및 합산

@tokeinizer_fsj 덕분에 의견의 의미를 완전히 얻지 못했다는 의견을 들었습니다 (추가는 두 개의 사전에서 다른 키를 추가하는 것을 의미한다고 생각했습니다. 합계해야합니다). 그래서 병합하기 전에 루프를 추가하여 두 번째 사전에 공통 키의 합계가 포함되도록했습니다. 마지막 사전은 두 개의 병합 결과 인 새 사전에서 값이 지속되는 사전이므로 문제가 해결됩니다. 이 솔루션은 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))

사전을 병합하는이 방법은 공통 키의 값을 추가하지 않습니다. 질문에서 원하는 키 값 b5(2 + 3)이지만 메소드가를 반환 3합니다.
tokenizer_fsj

4

또한 a.update( b )2 배 더 빠릅니다.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

2
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를 사용할 수 있습니다.


2

이것은 +=값에 적용될 수있는 두 개의 사전을 병합하기위한 간단한 솔루션이며 사전을 한 번만 반복해야합니다.

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}

1

이 솔루션은 사용하기 쉽고 일반 사전으로 사용되지만 합계 기능을 사용할 수 있습니다.

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

이건 어떤가요:

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}

0

위의 솔루션은 적은 수의 시나리오에 적합합니다 Counter. 그래도 큰 목록이 있다면 다음과 같은 것이 훨씬 좋습니다.

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

위의 솔루션은 본질적으로 Counters를 다음 과 같이 합산합니다 .

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

이것은 똑같은 일을하지만 항상 그것이 실제로 무엇을하고 있는지 보는 것이 도움이된다고 생각합니다.


0

다른 모듈이나 라이브러리없이 세 줄의 a, b, c를 한 줄에 병합

우리가 세 가지 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}

6
질문을 다시 읽으십시오. 이것은 예상되는 결과가 아닙니다. 입력 내용과 함께 있어야합니다 {'a': 9, 'b': 9, 'd': 90}.. "합계"요구 사항이 누락되었습니다.
Patrick Mevzek
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.