source

Python에서 중복된 dict in 목록 제거

gigabyte 2023. 2. 6. 23:38
반응형

Python에서 중복된 dict in 목록 제거

딕트 목록이 있는데 키와 값 쌍이 동일한 딕트를 삭제하고 싶습니다.

:[{'a': 123}, {'b': 123}, {'a': 123}]

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.[{'a': 123}, {'b': 123}]

또 다른 예는 다음과 같습니다.

:[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]

이것을 시험해 보세요.

[dict(t) for t in {tuple(d.items()) for d in l}]

이 전략은 사전 목록을 튜플 목록에 사전 항목이 포함되어 있는 튜플 목록으로 변환하는 것입니다.할 수 .set(여기서 일련의 이해를 사용하여 오래된 파이썬 대안은set(tuple(d.items()) for d in l)그런 다음 다음 Tuples에서 사전을 다시 만듭니다.dict.

여기서:

  • l입니다.
  • d 중 하나입니다.
  • t 중 입니다.

편집: 주문을 유지하려면 위의 한 줄만 표시해도 다음 시간 이후로는 작동하지 않습니다.set 됩니다. : 、 몇 、 몇 、 몇 、 몇 、 몇 、 몇 、 몇 、 몇 、 몇 음 、 음 음 음 음 also also also also also also also also also

l = [{'a': 123, 'b': 1234},
        {'a': 3222, 'b': 1234},
        {'a': 123, 'b': 1234}]

seen = set()
new_l = []
for d in l:
    t = tuple(d.items())
    if t not in seen:
        seen.add(t)
        new_l.append(d)

print new_l

출력 예:

[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]

주의: @alexis가 지적한 바와 같이 키와 값이 동일한 두 개의 사전이 동일한 태플이 되지 않을 수 있습니다.다른 키 추가/삭제 이력을 거치면 이 문제가 발생할 수 있습니다.있는 는, 「 「」의 정렬을 .d.items()그의 제안대로.

목록 합산을 기반으로 한 또 다른 한 줄:

>>> d = [{'a': 123}, {'b': 123}, {'a': 123}]
>>> [i for n, i in enumerate(d) if i not in d[n + 1:]]
[{'b': 123}, {'a': 123}]

에서는 ★★★★★★★★★★★★★★를 사용할 수 있기 때문에,dict첫 목록의 에 없는 이은 인덱스를 할 수 있습니다).n, , , , , , , , , 을 사용합니다.enumerate를 참조해 주세요.

서드파티 패키지를 사용해도 괜찮다면 다음을 사용할 수 있습니다.

>>> from iteration_utilities import unique_everseen
>>> l = [{'a': 123}, {'b': 123}, {'a': 123}]
>>> list(unique_everseen(l))
[{'a': 123}, {'b': 123}]

는 더 알고리즘예: 사전 등에한 항목을 할 수도 .O(n*m)서 ''는n이며, "는 " " " 입니다.m 내의 O(n)이 모두 )를 할 수 .key을 작성하기 인수(이 는 "에서 작동합니다).O(n)를 참조해 주세요.

않고 의되는 다른 .frozenset:

>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]

주의: 심플을 사용하면 안 됩니다.tuple동일한 사전의 순서가 반드시 같지는 않기 때문에 접근법(정렬하지 않음)은 절대 순서가 아닌 삽입 순서가 보장되는 Python 3.7에서도 마찬가지입니다.

>>> d1 = {1: 1, 9: 9}
>>> d2 = {9: 9, 1: 1}
>>> d1 == d2
True
>>> tuple(d1.items()) == tuple(d2.items())
False

또한 키를 정렬할 수 없으면 태플 정렬도 작동하지 않을 수 있습니다.

>>> d3 = {1: 1, 'a': 'a'}
>>> tuple(sorted(d3.items()))
TypeError: '<' not supported between instances of 'str' and 'int'

벤치마크

이러한 접근방식의 퍼포먼스가 어떻게 비교되는지 확인하는 것이 도움이 될 수 있다고 생각하여 간단한 벤치마크를 실시했습니다.벤치마크 그래프는 중복이 없는 목록을 기반으로 한 시간 대 목록 크기입니다(임의로 선택한 경우 중복이 일부 또는 많이 추가되어도 런타임은 크게 변경되지 않습니다).로그 로그 플롯이므로 전체 범위가 포함됩니다.

절대 시간:

여기에 이미지 설명 입력

가장 빠른 접근 방식에 대한 타이밍:

여기에 이미지 설명 입력

사방에서 두 번째 접근은 여기서 가장 빠릅니다.unique_everseen 가지고 접근하다key기능은 2위이지만 질서를 유지하는 가장 빠른 접근 방식입니다.jcolladofourtheye의 다른 접근은 거의 같은 속도입니다.를 사용한 접근법unique_everseen키가 없으면 Emmanuel과 Scorpil솔루션은 긴 목록에 비해 매우 느리고 훨씬 더 나쁘게 동작합니다.O(n*n)O(n). stpks 어프로치json 그래O(n*n)하지만 비슷한 것보다 훨씬 느리다.O(n)근합니니다다

벤치마크를 재현하기 위한 코드:

from simple_benchmark import benchmark
import json
from collections import OrderedDict
from iteration_utilities import unique_everseen

def jcollado_1(l):
    return [dict(t) for t in {tuple(d.items()) for d in l}]

def jcollado_2(l):
    seen = set()
    new_l = []
    for d in l:
        t = tuple(d.items())
        if t not in seen:
            seen.add(t)
            new_l.append(d)
    return new_l

def Emmanuel(d):
    return [i for n, i in enumerate(d) if i not in d[n + 1:]]

def Scorpil(a):
    b = []
    for i in range(0, len(a)):
        if a[i] not in a[i+1:]:
            b.append(a[i])

def stpk(X):
    set_of_jsons = {json.dumps(d, sort_keys=True) for d in X}
    return [json.loads(t) for t in set_of_jsons]

def thefourtheye_1(data):
    return OrderedDict((frozenset(item.items()),item) for item in data).values()

def thefourtheye_2(data):
    return {frozenset(item.items()):item for item in data}.values()

def iu_1(l):
    return list(unique_everseen(l))

def iu_2(l):
    return list(unique_everseen(l, key=lambda inner_dict: frozenset(inner_dict.items())))

funcs = (jcollado_1, Emmanuel, stpk, Scorpil, thefourtheye_1, thefourtheye_2, iu_1, jcollado_2, iu_2)
arguments = {2**i: [{'a': j} for j in range(2**i)] for i in range(2, 12)}
b = benchmark(funcs, arguments, 'list size')

%matplotlib widget
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.style.use('ggplot')
mpl.rcParams['figure.figsize'] = '8, 6'

b.plot(relative_to=thefourtheye_2)

완전성을 위해 중복된 목록만 포함하는 타이밍을 다음에 나타냅니다.

# this is the only change for the benchmark
arguments = {2**i: [{'a': 1} for j in range(2**i)] for i in range(2, 12)}

여기에 이미지 설명 입력

은 크게 unique_everseen 없이key이 경우 가장 빠른 해결책입니다. 이것은 캐시 의 값을 그 것은 다르기 때문입니다.O(n*m)에, 「1」로 합니다.O(n)


면책사항:나는 의 작가이다.iteration_utilities.

역직렬화된 JSON 개체와 같은 중첩된 사전에서 작업하는 경우 다른 응답은 작동하지 않습니다.이 경우 다음을 사용할 수 있습니다.

import json
set_of_jsons = {json.dumps(d, sort_keys=True) for d in X}
X = [json.loads(t) for t in set_of_jsons]

워크플로우에서 Panda를 사용하는 경우 한 가지 옵션은 사전 목록을 직접 제공하는 것입니다.pd.DataFrame컨스트럭터그런 다음 및 방법을 사용하여 필요한 결과를 얻습니다.

import pandas as pd

d = [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]

d_unique = pd.DataFrame(d).drop_duplicates().to_dict('records')

print(d_unique)

[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]

오래된 스타일의 루프가 여전히 유용할 수 있습니다.이 코드는 jcollado 코드보다 조금 길지만 읽기 매우 쉽습니다.

a = [{'a': 123}, {'b': 123}, {'a': 123}]
b = []
for i in range(len(a)):
    if a[i] not in a[i+1:]:
        b.append(a[i])

주문을 보존하려면 다음 작업을 수행합니다.

from collections import OrderedDict
print OrderedDict((frozenset(item.items()),item) for item in data).values()
# [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]

순서가 중요하지 않으면 다음 작업을 수행할 수 있습니다.

print {frozenset(item.items()):item for item in data}.values()
# [{'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]

일반적인 답은 아니지만, 다음과 같은 키별로 목록이 정렬될 경우:

l=[{'a': {'b': 31}, 't': 1},
   {'a': {'b': 31}, 't': 1},
 {'a': {'b': 145}, 't': 2},
 {'a': {'b': 25231}, 't': 2},
 {'a': {'b': 25231}, 't': 2}, 
 {'a': {'b': 25231}, 't': 2}, 
 {'a': {'b': 112}, 't': 3}]

솔루션은 다음과 같이 심플합니다.

import itertools
result = [a[0] for a in itertools.groupby(l)]

결과:

[{'a': {'b': 31}, 't': 1},
{'a': {'b': 145}, 't': 2},
{'a': {'b': 25231}, 't': 2},
{'a': {'b': 112}, 't': 3}]

중첩된 사전과 함께 작동하며 순서를 유지합니다.

세트를 사용할 수 있지만 dits를 해시 가능한 유형으로 변환해야 합니다.

seq = [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]
unique = set()
for d in seq:
    t = tuple(d.iteritems())
    unique.add(t)

유니크한 기능

set([(('a', 3222), ('b', 1234)), (('a', 123), ('b', 1234))])

받아쓰기를 되돌리려면:

[dict(x) for x in unique]

사전은 해시할 수 없으므로 목록의 각 항목을 문자열로 변환하는 것이 가장 쉬운 방법입니다.그런 다음 집합을 사용하여 중복 항목을 제거할 수 있습니다.

list_org = [{'a': 123}, {'b': 123}, {'a': 123}]
list_org_updated = [ str(item) for item in list_org]
print(list_org_updated)
["{'a': 123}", "{'b': 123}", "{'a': 123}"]
unique_set = set(list_org_updated)
print(unique_set)
{"{'b': 123}", "{'a': 123}"}

세트를 사용할 수 있지만 목록을 원할 경우 다음을 추가합니다.

import ast
unique_list = [ast.literal_eval(item) for item in unique_set]
print(unique_list)
[{'b': 123}, {'a': 123}]

다음은 목록 이해가 이중화된 간단한 한 줄 솔루션입니다(@Emanuel의 솔루션에 기반).

를 들어, 「」라고 하는 키)를합니다.a가 dict와 하는 것이 각 dict를

[i for n, i in enumerate(list_of_dicts) if i.get(primary_key) not in [y.get(primary_key) for y in list_of_dicts[n + 1:]]]

OP가 요구했던 것은 아니지만, 그것이 나를 이 상황에 이르게 한 이유이기 때문에, 나는 최종적인 솔루션을 투고하려고 생각했다.

짧지는 않지만 읽기 쉽다:

list_of_data = [{'a': 123}, {'b': 123}, {'a': 123}]

list_of_data_uniq = []
for data in list_of_data:
    if data not in list_of_data_uniq:
        list_of_data_uniq.append(data)

이제 목록, 목록list_of_data_uniq이치노

사용자 지정 키로 중복 제거:

def remove_duplications(arr, key):
    return list({key(x): x for x in arr}.values())

중복된 값과 키를 검색하는 많은 좋은 예를 다음에 제시하겠습니다.목록에 있는 전체 사전 중복 데이터를 필터링하는 방법은 다음과 같습니다.원본 데이터가 정확히 포맷된 사전으로 구성되어 있고 중복된 데이터를 찾는 경우 dupKeys = []를 사용합니다.그렇지 않으면 dupKeys =를 중복되지 않을 데이터의 키 이름으로 설정합니다(1 ~ n개 키).우아하지만 기능성과 유연성이 뛰어납니다.

import binascii

collected_sensor_data = [{"sensor_id":"nw-180","data":"XXXXXXX"},
                         {"sensor_id":"nw-163","data":"ZYZYZYY"},
                         {"sensor_id":"nw-180","data":"XXXXXXX"},
                         {"sensor_id":"nw-97", "data":"QQQQQZZ"}]

dupKeys = ["sensor_id", "data"]

def RemoveDuplicateDictData(collected_sensor_data, dupKeys):

    checkCRCs = []
    final_sensor_data = []
    
    if dupKeys == []:
        for sensor_read in collected_sensor_data:
            ck1 = binascii.crc32(str(sensor_read).encode('utf8'))
            if not ck1 in checkCRCs:
                final_sensor_data.append(sensor_read)
                checkCRCs.append(ck1)
    else:
        for sensor_read in collected_sensor_data:
            tmp = ""
            for k in dupKeys:
                tmp += str(sensor_read[k])

            ck1 = binascii.crc32(tmp.encode('utf8'))
            if not ck1 in checkCRCs:
                final_sensor_data.append(sensor_read)
                checkCRCs.append(ck1)
  
           
    return final_sensor_data    

 final_sensor_data = [{"sensor_id":"nw-180","data":"XXXXXXX"},
                      {"sensor_id":"nw-163","data":"ZYZYZYY"},
                      {"sensor_id":"nw-97", "data":"QQQQQZZ"}]
    

확장성과 뛰어난 퍼포먼스에 관심이 없다면 간단한 기능:

# Filters dicts with the same value in unique_key
# in: [{'k1': 1}, {'k1': 33}, {'k1': 1}]
# out: [{'k1': 1}, {'k1': 33}]
def remove_dup_dicts(list_of_dicts: list, unique_key) -> list:
    unique_values = list()
    unique_dicts = list()
    for obj in list_of_dicts:
        val = obj.get(unique_key)
        if val not in unique_values:
            unique_values.append(val)
            unique_dicts.append(obj)
    return unique_dicts

언급URL : https://stackoverflow.com/questions/9427163/remove-duplicate-dict-in-list-in-python

반응형