[python] 순서를 유지하면서 목록에서 중복 항목을 어떻게 제거합니까?

순서를 유지하면서 파이썬의 목록에서 중복을 제거하는 내장 기능이 있습니까? 중복 세트를 제거하기 위해 세트를 사용할 수는 있지만 원래 순서는 파괴됩니다. 또한 다음과 같이 나 자신을 굴릴 수 있다는 것을 알고 있습니다.

def uniq(input):
  output = []
  for x in input:
    if x not in output:
      output.append(x)
  return output

해당 코드 샘플풀어 주셔서 감사합니다 .

그러나 가능한 경우 내장 또는 더 많은 파이썬 관용구를 사용하고 싶습니다.

관련 질문 : 파이썬에서는 순서를 유지하면서 모든 요소가 고유하도록 목록에서 중복을 제거하는 가장 빠른 알고리즘은 무엇 입니까?



답변

여기 몇 가지 대안이 있습니다. http://www.peterbe.com/plog/uniqifiers-benchmark

가장 빠른 것 :

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

그냥 전화 seen.add하는 seen_add대신에 할당 하는 이유는 무엇 seen.add입니까? 파이썬은 동적 언어이며, seen.add각 반복을 해결하는 것은 지역 변수를 해결하는 것보다 비용이 많이 듭니다. seen.add반복 사이에서 변경되었을 수 있으며 런타임은 그것을 배제 할만 큼 똑똑하지 않습니다. 안전하게 플레이하려면 매번 물체를 확인해야합니다.

동일한 데이터 세트에서이 기능을 많이 사용하려는 경우 다음과 같이 정렬 된 세트를 사용하는 것이 좋습니다. http://code.activestate.com/recipes/528878/

작업 당 O (1) 삽입, 삭제 및 구성원 확인.

(작은 추가 참고 사항 : seen.add()항상을 반환 None하므로 or위의 내용은 논리적 테스트의 필수 부분이 아닌 세트 업데이트를 시도하는 방법으로 만 사용됩니다.)


답변

2016 년 편집

Raymond가 지적했듯이OrderedDict C로 구현 된 python 3.5 이상에서는 목록 이해 접근 방식이 느립니다 OrderedDict(실제로 목록이 필요하지 않은 한 입력이 매우 짧은 경우에만). 따라서 3.5+에 가장 적합한 솔루션은 OrderedDict입니다.

중요 편집 2015

@abarnert가 지적 했듯이 more_itertools라이브러리 ( pip install more_itertools)에는 목록 이해에서 읽을 수없는 ( ) 돌연변이unique_everseen 없이이 문제를 해결하기 위해 작성된 함수가 포함되어 있습니다 . 이것은 또한 가장 빠른 솔루션입니다.not seen.add

>>> from  more_itertools import unique_everseen
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(unique_everseen(items))
[1, 2, 0, 3]

단 하나의 간단한 라이브러리 가져 오기 및 해킹 없음. 이것은 itertools 레시피의 구현에서 비롯됩니다 unique_everseen.

def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in filterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

파이썬 2.7+에서 허용되는 일반적인 관용구 (작동하지만 속도에 최적화되지 않았으므로 이제는 unique_everseen)를 사용합니다 collections.OrderedDict.

런타임 : O (N)

>>> from collections import OrderedDict
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]

이것은 다음보다 훨씬 멋지게 보입니다.

seen = set()
[x for x in seq if x not in seen and not seen.add(x)]

못생긴 해킹을 사용하지 않습니다 .

not seen.add(x)

이는 set.add항상 반환되는 인플레 이스 (in-place) 메소드라는 사실에 의존 None하므로 not None평가됩니다 True.

그러나 해킹 솔루션은 동일한 런타임 복잡도 O (N)를 갖지만 원시 속도가 더 빠릅니다.


답변

Python 2.7 에서 원래 순서대로 유지하면서 iterable에서 중복을 제거하는 새로운 방법은 다음과 같습니다.

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

Python 3.5 에서 OrderedDict에는 C 구현이 있습니다. 내 타이밍에 따르면 이것이 현재 Python 3.5에 대한 다양한 접근 방식 중 가장 빠르고 가장 짧습니다.

Python 3.6 에서는 일반 dict이 순서가 작고 간결 해졌습니다. (이 기능은 CPython 및 PyPy 용이지만 다른 구현에는 없을 수 있습니다). 이를 통해 주문을 유지하면서 새로운 가장 빠른 중복 제거 방법을 얻을 수 있습니다.

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

Python 3.7 에서는 일반 구현이 모든 구현에서 순서대로 보장됩니다. 가장 짧고 빠른 솔루션은 다음과 같습니다.

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

@max에 대한 응답 : 3.6 또는 3.7로 이동하고 OrderedDict 대신 일반 dict를 사용하면 다른 방식으로 성능을 실제로 능가 할 수 없습니다. 사전은 밀도가 높으며 오버 헤드가 거의없는 목록으로 쉽게 변환됩니다. 대상 목록은 len (d)로 사전 크기 조정되어 목록 이해에서 발생하는 모든 크기 조정을 저장합니다. 또한 내부 키 목록은 밀도가 높으므로 포인터를 복사하는 것이 목록 복사와 거의 같습니다.


답변

sequence = ['1', '2', '3', '3', '6', '4', '5', '6']
unique = []
[unique.append(item) for item in sequence if item not in unique]

고유 → ['1', '2', '3', '6', '4', '5']


답변

죽은 말을 걷지 말아라 (이 질문은 매우 오래되었고 이미 많은 정답이 있습니다). 여기에 많은 상황에서 매우 빠르며 사용하기가 쉽지 않은 팬더를 사용하는 솔루션이 있습니다.

import pandas as pd

my_list = [0, 1, 2, 3, 4, 1, 2, 3, 5]

>>> pd.Series(my_list).drop_duplicates().tolist()
# Output:
# [0, 1, 2, 3, 4, 5]


답변

from itertools import groupby
[ key for key,_ in groupby(sortedList)]

목록을 정렬 할 필요조차 없으며 충분한 조건은 동일한 값이 함께 그룹화된다는 것입니다.

편집 : “보존 순서”는 목록이 실제로 정렬되어 있음을 의미한다고 가정했습니다. 그렇지 않은 경우 MizardX의 솔루션이 적합합니다.

커뮤니티 편집 : 그러나 이것은 “중복 연속 요소를 단일 요소로 압축하는”가장 우아한 방법입니다.


답변

주문을 유지하고 싶다면

당신은 이것을 시도 할 수 있습니다 :

list1 = ['b','c','d','b','c','a','a']
list2 = list(set(list1))
list2.sort(key=list1.index)
print list2

또는 이와 유사하게 다음을 수행 할 수 있습니다.

list1 = ['b','c','d','b','c','a','a']
list2 = sorted(set(list1),key=list1.index)
print list2 

당신은 또한 이것을 할 수 있습니다 :

list1 = ['b','c','d','b','c','a','a']
list2 = []
for i in list1:
    if not i in list2:
        list2.append(i)`
print list2

다음과 같이 쓸 수도 있습니다 :

list1 = ['b','c','d','b','c','a','a']
list2 = []
[list2.append(i) for i in list1 if not i in list2]
print list2