source

파이썬에서 두 사전의 차이를 얻는 방법은 무엇입니까?

ittop 2023. 7. 16. 17:55
반응형

파이썬에서 두 사전의 차이를 얻는 방법은 무엇입니까?

저는 두 개의 사전을 가지고 있고, 두 개의 차이점을 찾아야 하는데, 그것은 저에게 열쇠와 가치를 둘 다 줄 것입니다.

datadiff, dictdiff-master와 같은 추가/패키지를 검색하여 찾았는데, Python 2.7에서 가져오려고 하면 해당 모듈이 정의되어 있지 않다고 나옵니다.

나는 여기서 세트를 사용했습니다.

first_dict = {}
second_dict = {}
 
value = set(second_dict) - set(first_dict)
print value

내 출력은 다음과 같습니다.

>>> set(['SCD-3547', 'SCD-3456'])

저는 열쇠만 받고 있고, 값도 받아야 합니다.

그러기 위해서는 집합의 대칭적인 차이 연산을 사용하는 것이 좋다고 생각합니다. 여기 문서에 대한 링크가 있습니다.

>>> dict1 = {1:'donkey', 2:'chicken', 3:'dog'}
>>> dict2 = {1:'donkey', 2:'chimpansee', 4:'chicken'}
>>> set1 = set(dict1.items())
>>> set2 = set(dict2.items())
>>> set1 ^ set2
{(2, 'chimpansee'), (4, 'chicken'), (2, 'chicken'), (3, 'dog')}

대칭적인 이유는 다음과 같습니다.

>>> set2 ^ set1
{(2, 'chimpansee'), (4, 'chicken'), (2, 'chicken'), (3, 'dog')}

차분 연산자를 사용하는 경우에는 그렇지 않습니다.

>>> set1 - set2
{(2, 'chicken'), (3, 'dog')}
>>> set2 - set1
{(2, 'chimpansee'), (4, 'chicken')}

그러나 정보가 손실될 수 있으므로 결과 집합을 사전으로 변환하는 것은 좋지 않을 수 있습니다.

>>> dict(set1 ^ set2)
{2: 'chicken', 3: 'dog', 4: 'chicken'}

사전 이해를 사용하여 다음 스니펫을 시도해 보십시오.

value = { k : second_dict[k] for k in set(second_dict) - set(first_dict) }

위의 코드에서 우리는 키의 차이를 찾은 다음 다시 작성합니다.dict해당하는 값을 취합니다.

다른 은 또다해은책결입니다.dictdiffer(https://github.com/inveniosoftware/dictdiffer) .

import dictdiffer                                          

a_dict = {                                                 
  'a': 'foo',
  'b': 'bar',
  'd': 'barfoo'
}                                                          

b_dict = {                                                 
  'a': 'foo',                                              
  'b': 'BAR',
  'c': 'foobar'
}                                                          

for diff in list(dictdiffer.diff(a_dict, b_dict)):         
    print diff

diff는 변경 유형, 변경된 값 및 항목의 경로를 가진 튜플입니다.

('change', 'b', ('bar', 'BAR'))
('add', '', [('c', 'foobar')])
('remove', '', [('d', 'barfoo')])

DeepDiff를 사용할 수 있습니다.

pip install deepdiff

특히 사전, 반복 가능한 항목, 문자열 및 기타 개체의 차이를 재귀적으로 계산할 수 있습니다.

>>> from deepdiff import DeepDiff

>>> d1 = {1:1, 2:2, 3:3, "foo":4}
>>> d2 = {1:1, 2:4, 3:3, "bar":5, 6:6}
>>> DeepDiff(d1, d2)
{'dictionary_item_added': [root['bar'], root[6]],
 'dictionary_item_removed': [root['foo']],
 'values_changed': {'root[2]': {'new_value': 4, 'old_value': 2}}}

변경된 항목(짝수 유형), 추가된 항목 및 제거된 항목을 볼 수 있습니다.또한 중복을 무시하고 경로를 무시하는 등의 다양한 작업을 수행할 수 있습니다(정규식으로 정의됨).

해결책은 다음을 사용하는 것입니다.unittest모듈:

from unittest import TestCase
TestCase().assertDictEqual(expected_dict, actual_dict)

에서 얻은의 사전이 파이썬에서 파이테스트와 동일한지 테스트할 수 있는 방법

세트를 사용하는 것을 보는 것이 옳았습니다, 우리는 당신의 방법을 작동시키기 위해 조금 더 깊이 파고들 필요가 있습니다.

먼저 예제 코드:

test_1 = {"foo": "bar", "FOO": "BAR"}
test_2 = {"foo": "bar", "f00": "b@r"}

두 사전에 모두 유사한 키/값 쌍이 포함되어 있음을 확인할 수 있습니다.

{"foo": "bar", ...}

또한 각 사전에는 완전히 다른 키 값 쌍이 포함되어 있습니다.하지만 어떻게 차이를 감지할 수 있을까요?사전은 이를 지원하지 않습니다.대신에, 당신은 세트를 사용하기를 원할 것입니다.

다음은 각 사전을 사용할 수 있는 집합으로 변환하는 방법입니다.

set_1 = set(test_1.items())
set_2 = set(test_2.items())

일련의 튜플을 포함하는 집합을 반환합니다.각 튜플은 사전에서 하나의 키/값 쌍을 나타냅니다.

이제 set_1과 set_2의 차이를 찾으려면:

print set_1 - set_2
>>> {('FOO', 'BAR')}

사전을 돌려드릴까요?쉬운, 그냥:

dict(set_1 - set_2)
>>> {'FOO': 'BAR'}

저는 좋은 개발자들이 이미 작성한 것을 사용하는 것을 추천합니다.맘에 들다pytest그것은 딕트뿐만 아니라 모든 데이터 유형과 거래합니다.BTW, 그고리 BTW,pytest테스트를 매우 잘합니다.

from _pytest.assertion.util import _compare_eq_any

print('\n'.join(_compare_eq_any({'a': 'b'}, {'aa': 'vv'}, verbose=3)))

출력:

Left contains 1 more item:
{'a': 'b'}
Right contains 1 more item:
{'aa': 'vv'}
Full diff:
- {'aa': 'vv'}
?    -    ^^
+ {'a': 'b'}
?        ^

기능을 하는 것이 (으로 )_), 의 코드에 하십시오.), "Copy/Copy"입니다.

추신: 테트대로 pytest==6.2.4

다른 답변에 언급된 것처럼 대칭 차분 집합 연산자를 사용하는 함수로, 값의 원점을 보존합니다.

def diff_dicts(a, b, missing=KeyError):
    """
    Find keys and values which differ from `a` to `b` as a dict.

    If a value differs from `a` to `b` then the value in the returned dict will
    be: `(a_value, b_value)`. If either is missing then the token from 
    `missing` will be used instead.

    :param a: The from dict
    :param b: The to dict
    :param missing: A token used to indicate the dict did not include this key
    :return: A dict of keys to tuples with the matching value from a and b
    """
    return {
        key: (a.get(key, missing), b.get(key, missing))
        for key in dict(
            set(a.items()) ^ set(b.items())
        ).keys()
    }

print(diff_dicts({'a': 1, 'b': 1}, {'b': 2, 'c': 2}))

# {'c': (<class 'KeyError'>, 2), 'a': (1, <class 'KeyError'>), 'b': (1, 2)}

작동 방식

우리는 항목을 취하면서 생성된 튜플에 대칭 차이 집합 연산자를 사용합니다.의 구별되는 합니다.(key, value)두 받아쓰기의 튜플

그런 다음 새로운 명령어를 만들어 키를 함께 접고 반복합니다.이 키들은 하나의 딕트에서 다음 딕트로 변경된 유일한 키입니다.

그런 다음 이러한 키를 사용하여 키가 없을 때 누락된 토큰을 대체하는 각 딕트의 값 튜플과 함께 새 딕트를 구성합니다.

이 기능은 사전 키만을 기준으로 모든 차이(및 동일하게 유지된 차이)를 제공합니다.또한 일부 멋진 딕트 이해, 세트 작업 및 파이썬 3.6 유형 주석을 강조합니다 :)

from typing import Dict, Any, Tuple
def get_dict_diffs(a: Dict[str, Any], b: Dict[str, Any]) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any], Dict[str, Any]]:

    added_to_b_dict: Dict[str, Any] = {k: b[k] for k in set(b) - set(a)}
    removed_from_a_dict: Dict[str, Any] = {k: a[k] for k in set(a) - set(b)}
    common_dict_a: Dict[str, Any] = {k: a[k] for k in set(a) & set(b)}
    common_dict_b: Dict[str, Any] = {k: b[k] for k in set(a) & set(b)}
    return added_to_b_dict, removed_from_a_dict, common_dict_a, common_dict_b

사전 값을 비교하려는 경우:

values_in_b_not_a_dict = {k : b[k] for k, _ in set(b.items()) - set(a.items())}

이것은 https://stackoverflow.com/a/67263119/919692 과 https://stackoverflow.com/a/48544451/919692, 을 결합한 저만의 버전이며, 이제 https://stackoverflow.com/a/47433207/919692 과 상당히 유사하다는 것을 알게 되었습니다.

def dict_diff(dict_a, dict_b, show_value_diff=True):
  result = {}
  result['added']   = {k: dict_b[k] for k in set(dict_b) - set(dict_a)}
  result['removed'] = {k: dict_a[k] for k in set(dict_a) - set(dict_b)}
  if show_value_diff:
    common_keys =  set(dict_a) & set(dict_b)
    result['value_diffs'] = {
      k:(dict_a[k], dict_b[k])
      for k in common_keys
      if dict_a[k] != dict_b[k]
    }
  return result

이것이 OP가 요구한 것인지는 확실하지 않지만, 제가 이 질문을 접했을 때 찾고 있던 것입니다. 구체적으로, 키별로 두 딕트의 차이를 보여주는 방법입니다.

Pitfall: 하나의 딕트에 누락된 키가 있고 두 번째 딕트에 없음 값이 있을 때 함수는 그들이 유사하다고 가정합니다.

이것은 전혀 최적화되지 않았습니다. 작은 딕트에 적합합니다.

def diff_dicts(a, b, drop_similar=True):
    res = a.copy()

    for k in res:
        if k not in b:
            res[k] = (res[k], None)

    for k in b:
        if k in res:
            res[k] = (res[k], b[k])
        else:
            res[k] = (None, b[k])

    if drop_similar:
        res = {k:v for k,v in res.items() if v[0] != v[1]}

    return res


print(diff_dicts({'a': 1}, {}))
print(diff_dicts({'a': 1}, {'a': 2}))
print(diff_dicts({'a': 2}, {'a': 2}))
print(diff_dicts({'a': 2}, {'b': 2}))
print(diff_dicts({'a': 2}, {'a': 2, 'b': 1}))

출력:

{'a': (1, None)}
{'a': (1, 2)}
{}
{'a': (2, None), 'b': (None, 2)}
{'b': (None, 1)}

이건 어때요?그렇게 예쁘지는 않지만 노골적입니다.

orig_dict = {'a' : 1, 'b' : 2}
new_dict = {'a' : 2, 'v' : 'hello', 'b' : 2}

updates = {}
for k2, v2 in new_dict.items():
    if k2 in orig_dict:    
        if v2 != orig_dict[k2]:
            updates.update({k2 : v2})
    else:
        updates.update({k2 : v2})

#test it
#value of 'a' was changed
#'v' is a completely new entry
assert all(k in updates for k in ['a', 'v'])
def flatten_it(d):
    if isinstance(d, list) or isinstance(d, tuple):
        return tuple([flatten_it(item) for item in d])
    elif isinstance(d, dict):
        return tuple([(flatten_it(k), flatten_it(v)) for k, v in sorted(d.items())])
    else:
        return d

dict1 = {'a': 1, 'b': 2, 'c': 3}
dict2 = {'a': 1, 'b': 1}

print set(flatten_it(dict1)) - set(flatten_it(dict2)) # set([('b', 2), ('c', 3)])
# or 
print set(flatten_it(dict2)) - set(flatten_it(dict1)) # set([('b', 1)])

오래된 질문이지만 어쨌든 해결책을 공유해야겠다고 생각했습니다.아주 간단합니다.

dicta_set = set(dicta.items()) # creates a set of tuples (k/v pairs)
dictb_set = set(dictb.items())
setdiff = dictb_set.difference(dicta_set) # any set method you want for comparisons
for k, v in setdiff: # unpack the tuples for processing
    print(f"k/v differences = {k}: {v}")

이 코드는 k/v 쌍을 나타내는 두 개의 튜플 세트를 생성합니다.그런 다음 선택한 설정된 방법을 사용하여 튜플을 비교합니다.마지막으로 처리를 위해 튜플(k/v 쌍)의 포장을 풉니다.

새 딕트(변경된 데이터만)가 반환됩니다.

def get_difference(obj_1: dict, obj_2: dict) -> dict:
result = {}

for key in obj_1.keys():
    value = obj_1[key]

    if isinstance(value, dict):
        difference = get_difference(value, obj_2.get(key, {}))

        if difference:
            result[key] = difference

    elif value != obj_2.get(key):
        result[key] = obj_2.get(key, None)

return result

한쪽 비교를 위해 딕트 이해를 사용할 수 있습니다.

dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
dict2 = {'a': OMG, 'b': 2, 'c': 3, 'd': 4}

data = {a:dict1[a] for a in dict1 if dict1[a] != dict2[a]}

출력: {'a': 1}

다음은 dict2의 값이 올바른 경우 dict1 값을 업데이트할 수 있는 변형입니다.

고려 사항:

dict1.update((k, dict2.get(k)) for k, v in dict1.items())
a_dic={'a':1, 'b':2}
b_dic={'a':1, 'b':20}

sharedmLst = set(a_dic.items()).intersection(b_dic.items())
diff_from_b = set(a_dic.items()) - sharedmLst
diff_from_a = set(b_dic.items()) - sharedmLst

print("Among the items in a_dic, the item different from b_dic",diff_from_b)
print("Among the items in b_dic, the item different from a_dic",diff_from_a)

Result :
Among the items in a_dic, the item different from b_dic {('b', 2)}
Among the items in b_dic, the item different from a_dic {('b', 20)}

이 솔루션은 해시할 수 없는 딕트와 완벽하게 작동하여 다음 오류를 해결합니다.

TypeError: Unhashable type 'dict'.

@Roedy의 최상위 솔루션부터 시작하십시오.목록 사전을 만듭니다. 이 사전은 해시할 수 없는 목록의 좋은 예입니다.

>>> dict1 = {1:['donkey'], 2:['chicken'], 3:['dog']}
>>> dict2 = {1:['donkey'], 2:['chimpansee'], 4:['chicken']}

그런 다음 다음 다음을 사용하여 각 값을 해시할 수 있도록 사전 처리합니다.str(value):

>>> set1 = set([(key, str(value)) for key, value in dict1.items()])
>>> set2 = set([(key, str(value)) for key, value in dict2.items()])

그런 다음 @Reody의 답변에 따라 계속 진행합니다.

>>> set1 ^ set2
{(3, "['dog']"), (4, "['chicken']"), (2, "['chimpansee']"), (2,"['chicken']")}

테스트를 위해 데이터 테스트 패키지는 사전, numpy 배열, 팬더 데이터 프레임 등의 차이를 확인합니다.데이터 검정을 사용하면 부동 소수점 비교에 대한 공차도 설정할 수 있습니다.

from datatest import validate, accepted
def test_compare_dict():
    expected = {"key1": 0.5}
    actual = {"key1": 0.499}
    with accepted.tolerance(0.1):
        validate(expected, actual)

차이는 다음과 같습니다.datatest.ValidationError유효하지 않음, 편차, 누락 또는 추가 항목이 포함됩니다.

언급URL : https://stackoverflow.com/questions/32815640/how-to-get-the-difference-between-two-dictionaries-in-python

반응형