목록에서 중복 제거


995

목록에 중복 항목이 있는지 확인하고 중복 여부를 확인하고 복제 / 제거되지 않은 항목이 포함 된 새 목록을 반환하는 프로그램을 작성해야합니다. 이것이 내가 가진 것이지만 정직하게 무엇을 해야할지 모르겠습니다.

def remove_duplicates():
    t = ['a', 'b', 'c', 'd']
    t2 = ['a', 'c', 'd']
    for t in t2:
        t.append(t.remove())
    return t

22
설명에 따르면 "목록"에 중복이 있는지 확인하지만 코드에서 두 목록을 확인합니다.
Brendan Long


* set 사용 : list (set (ELEMENTS_LIST)) * Dictionary 사용 : list (dict.fromkeys (ELEMENTS_LIST))
Shayan Amani

답변:


1640

고유 한 항목 모음을 얻는 일반적인 방법은을 사용하는 것 set입니다. 세트는 고유 한 개체 의 순서없는 모음입니다 . iterable에서 세트를 만들려면 간단히 내장 함수에 전달하면됩니다 . 나중에 실제 목록을 다시 필요로하는 경우에도 마찬가지로 세트를 함수에 전달할 수 있습니다 .set()list()

다음 예제는 수행하려는 모든 것을 다루어야합니다.

>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> list(set(t))
[1, 2, 3, 5, 6, 7, 8]
>>> s = [1, 2, 3]
>>> list(set(t) - set(s))
[8, 5, 6, 7]

예제 결과에서 볼 수 있듯이 원래 순서는 유지되지 않습니다 . 위에서 언급했듯이 세트 자체는 정렬되지 않은 컬렉션이므로 순서가 손실됩니다. 세트를 목록으로 다시 변환하면 임의의 순서가 작성됩니다.

질서 유지

순서가 중요하다면 다른 메커니즘을 사용해야합니다. 이를위한 가장 일반적인 해결책은 OrderedDict삽입 중에 키 순서를 유지하는 것입니다.

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Python 3.7부터 내장 사전은 삽입 순서를 유지하도록 보장되므로 Python 3.7 이상 (또는 CPython 3.6)을 사용하는 경우 직접 사용할 수도 있습니다.

>>> list(dict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

사전을 먼저 작성한 다음 목록을 작성하는 데 약간의 오버 헤드가있을 수 있습니다. 실제로 주문을 보존 할 필요가없는 경우, 특히 더 많은 작업을 수행 할 수 있기 때문에 세트를 사용하는 것이 좋습니다. 중복을 제거 할 때 순서를 유지하는 자세한 방법과 다른 방법은 이 질문 을 확인하십시오 .


마지막으로 / 솔루션 setOrderedDict/ dict솔루션 모두 아이템을 해시 할 수 있어야 합니다. 이것은 보통 불변이어야 함을 의미합니다. 해시 가능하지 않은 항목 (예 : 목록 개체)을 처리해야하는 경우 기본적으로 중첩 된 루프의 모든 다른 항목과 모든 항목을 비교해야하는 느린 접근 방식을 사용해야합니다.


4
해시 불가능한 목록 요소 (예 : 목록 목록)에는 작동하지 않습니다.
KNejad

3
@KNejad 이것이 바로 마지막 단락의 내용입니다.
찌를

오 이런 모든 것을 읽었을 것입니다. 내가 한 일은 목록 대신 튜플을 사용 하여이 접근법이 여전히 작동 할 수있었습니다.
KNejad

이 예를 t = [3, 2, 1, 1, 2, 5, 6, 7, 8]에 추가하면 차이가 명확하게 표시됩니다!
sailfish009

"... 먼저 사전을 작성하는 오버 헤드 ... 실제로 주문을 보존 할 필요가 없으면 세트를 사용하는 것이 좋습니다." — 이것이 사실인지 궁금하기 때문에 프로파일 링했습니다. 내 타이밍은 실제로 세트가 약간 더 빠르다는 것을 보여줍니다 .1M 루프 당 1.12 µs (세트) 대 1M 루프에 대해 루프 당 1.53 µs (dict)는 1M 반복에서 약 4 초의 절대 시간차를 갖습니다. 따라서 단단한 내부 루프 에서이 작업을 수행하는 경우 신경 쓰지 않을 수도 있습니다.
millerdev

414

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']

10
이것이 아이템을 순서대로 유지하는 유일한 방법이라고 생각합니다.
Herberth Amaral


5
@MartijnPieters 렉팅 :이 유일한 생각 간단한 순서로 항목을 유지하는 방법.
Herberth Amaral

11
이를 위해 원본 목록의 내용은 해시 가능해야합니다
Davide

@Davide가 언급했듯이 원래 목록은 해시 가능해야합니다. 이것은 사전 목록에 대해서는 작동하지 않음을 의미합니다. TypeError: unhashable type: 'dictlist'
CraZ

187

그것은 하나의 라이너입니다 : list(set(source_list))트릭을 할 것입니다.

A set는 중복 될 수없는 것입니다.

업데이트 : 주문 보존 접근 방식은 두 줄입니다.

from collections import OrderedDict
OrderedDict((x, True) for x in source_list).keys()

여기서 우리 OrderedDict는 키의 삽입 순서 를 기억하고 특정 키의 값이 업데이트 될 때 키를 변경하지 않는다는 사실을 사용합니다 . 우리 True는 값으로 삽입 하지만 아무것도 삽입 할 수 없으며 값은 사용되지 않습니다. ( 무시 된 값을 가진 set것과 비슷하게 작동합니다 dict.)


4
source_list해시 가능한 경우에만 작동합니다 .
Adrian Keister

@AdrianKeister : 사실입니다. 합리적인 동등 의미론을 가지고 있지만 해시 가능하지 않은 객체가 있습니다 (예 : 목록). OTOH 우리가 hastable과 같은 지름길을 가질 수 없다면, 모든 요소를 ​​현재 알려진 모든 고유 요소와 비교하는 이차 알고리즘으로 끝납니다. 짧은 입력, 특히 많은 중복을 가진 경우에는 완전히 괜찮을 수 있습니다.
9000

맞아요 이 일반적인 사용 사례를 고려하면 답변의 품질이 높아질 것이라고 생각합니다.
Adrian Keister

94
>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> s = []
>>> for i in t:
       if i not in s:
          s.append(i)
>>> s
[1, 2, 3, 5, 6, 7, 8]

33
이 방법은 O (n ^ 2) 시간에 작동하므로 큰 목록에서는 매우 느립니다.
dotancohen

@Chris_Rands : 확실하지 frozenset않은 해싱 가능 콘텐츠와 작동 하지 않습니다 . 를 사용할 때 여전히 해시 불가능 오류가 발생 frozenset합니다.
Adrian Keister

85

주문에 신경 쓰지 않으면 다음과 같이하십시오.

def remove_duplicates(l):
    return list(set(l))

A set는 중복되지 않아야합니다.


3
l해시 가능 하지 않으면 작동하지 않습니다 .
Adrian Keister

41

중복의 첫 번째 요소의 순서를 유지하면서 새 목록을 만들려면 L

newlist=[ii for n,ii in enumerate(L) if ii not in L[:n]]

예를 들어 if L=[1, 2, 2, 3, 4, 2, 4, 3, 5]다음 newlist될 것입니다[1,2,3,4,5]

이것은 추가하기 전에 각각의 새로운 요소가 목록에 이전에 나타나지 않았는지 확인합니다. 또한 수입이 필요하지 않습니다.


3
시간 복잡도는 O (n ^ 2) 입니다. 에 대한 답변 setOrderedDict낮은 상각 시간 복잡도가있을 수 있습니다.
blubberdiblub

필자는이 솔루션을 내 코드에서 사용했고 훌륭하게 작동했지만 시간이 많이
걸린다고

@blubberdiblub set 및 OrderedDict에 더 많은 코드 효율적인 메커니즘이 존재하여 시간을 덜 소비 할 수있는 방법을 설명 할 수 있습니까? (그들을로드의 오버 헤드 제외)
ILIAS는 일리아 디스

@iliasiliadis setdict 의 일반적인 구현은 해시 또는 (일부 형태의 균형 잡힌) 트리를 사용합니다. 집합 또는 dict을 빌드 하고 검색 하는 것을 고려해야 하지만 (복수), 상각 복잡도는 여전히 O (n ^ 2) 보다 낮습니다 . 간단한 용어로 "상각"은 평균을 의미합니다 (평균보다 복잡성이 높은 최악의 경우가있을 수 있음). 항목 수가 많은 경우에만 관련이 있습니다.
blubberdiblub

25

동료가 오늘 코드 검토를 위해 자신의 코드의 일부로 허용 된 답변을 보냈습니다. 해당 답변의 우아함을 확실히 존경하지만 공연에 만족하지 않습니다. 이 솔루션을 사용해 보았습니다 ( 검색 시간을 줄이기 위해 설정 사용 )

def ordered_set(in_list):
    out_list = []
    added = set()
    for val in in_list:
        if not val in added:
            out_list.append(val)
            added.add(val)
    return out_list

효율성을 비교하기 위해 100 개의 정수로 구성된 임의 샘플을 사용했습니다. 62는 고유했습니다.

from random import randint
x = [randint(0,100) for _ in xrange(100)]

In [131]: len(set(x))
Out[131]: 62

다음은 측정 결과입니다

In [129]: %timeit list(OrderedDict.fromkeys(x))
10000 loops, best of 3: 86.4 us per loop

In [130]: %timeit ordered_set(x)
100000 loops, best of 3: 15.1 us per loop

솔루션에서 세트를 제거하면 어떻게됩니까?

def ordered_set(inlist):
    out_list = []
    for val in inlist:
        if not val in out_list:
            out_list.append(val)
    return out_list

결과는 OrderedDict 만큼 나쁘지는 않지만 원래 솔루션의 3 배 이상입니다.

In [136]: %timeit ordered_set(x)
10000 loops, best of 3: 52.6 us per loop

루프 비교를 빠르게하기 위해 빠른 검색을 사용하면 좋습니다. 순서가 중요하지 않은 경우 list (set (x))가 여전히 이것보다 6 배 빠릅니다
Joop

@Joop, 그것은 나의 동료에게 나의 첫 번째 질문이었다 – 순서는 중요하다; 그렇지 않으면 사소한 문제 였을 것입니다
화산

관심있는 사람을위한 최적화 된 버전의 주문 세트 : def unique(iterable):; seen = set(); seen_add = seen.add; return [item for item in iterable if not item in seen and not seen_add(item)]
DrD

25

Pandas와 Numpy를 사용하는 솔루션도 있습니다. 둘 다 numpy 배열을 반환하므로 .tolist()목록을 원하면 함수를 사용해야 합니다.

t=['a','a','b','b','b','c','c','c']
t2= ['c','c','b','b','b','a','a','a']

팬더 솔루션

팬더 기능 사용 unique():

import pandas as pd
pd.unique(t).tolist()
>>>['a','b','c']
pd.unique(t2).tolist()
>>>['c','b','a']

너피 솔루션

numpy 함수 사용 unique().

import numpy as np
np.unique(t).tolist()
>>>['a','b','c']
np.unique(t2).tolist()
>>>['a','b','c']

numpy.unique ()도 값을 정렬합니다 . 따라서 목록 t2이 정렬되어 반환됩니다. 이 답변 에서와 같이 순서를 유지하려면 다음을 수행하십시오 .

_, idx = np.unique(t2, return_index=True)
t2[np.sort(idx)].tolist()
>>>['c','b','a']

이 솔루션은 다른 솔루션에 비해 그렇게 우아하지는 않지만 pandas.unique ()와 비교할 때 numpy.unique ()를 사용하면 중첩 배열이 하나의 선택된 축을 따라 고유한지 확인할 수도 있습니다.


이것은 목록을 numpy 배열로 변환하여 엉망이며 문자열에는 작동하지 않습니다.
user227666

1
@ user227666 귀하의 검토에 감사하지만 그것은 사실이 아닙니다 그것은 문자열에서도 작동하며 목록을 얻으려면 .tolist를 추가 할 수 있습니다 ...
GM

1
나는 이것이 쇠 망치로 꿀벌을 죽이려고하는 것과 비슷하다고 생각합니다. 확실히 작동합니다! 그러나이 목적을 위해 라이브러리를 가져 오는 것은 약간 과잉 일 수 있습니다.
Debosmit Ray

@DebosmitRay 일반적으로 numpy로 작업하고 numpy 배열로 여러 번 작업 해야하는 Data Science에서 작업하는 경우 유용 할 수 있습니다.
GM

난 당신이 당신의 마음과 사용 NumPy와 변경 희망 @DebosmitRay 2020 년 최고의 대답은 / 당신이 할 수있는 모든 시간 팬더
자존심

21

다른 방법 :

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]

1
현대 파이썬 버전 (2.7 이상) keys()은 목록이 아니라 사전보기 객체를 반환합니다.
Dustin Wyatt

16

간단하고 쉬운 :

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanlist = []
[cleanlist.append(x) for x in myList if x not in cleanlist]

산출:

>>> cleanlist 
[1, 2, 3, 5, 6, 7, 8]

5
그럼에도 불구하고 이차 복잡성 in-O (n) 연산이며 cleanlist최대 n개수 => 최악의 경우 ~ O (n ^ 2)
jermenkoo

6
부작용에 목록 이해력을 사용해서는 안됩니다.
Jean-François Fabre

13

이 답변에는 두 가지 섹션이 있습니다. 두 가지 고유 한 솔루션과 특정 솔루션의 속도 그래프.

중복 항목 제거

이러한 답변의 대부분은 전용 중복 항목 제거 해쉬을 하지만, 그것을 의미하지 않습니다이 질문은 필요하지 않습니다 해쉬 내가 필요하지 않은 몇 가지 솔루션을 제공 할 것입니다 의미 항목을 해쉬 항목을.

카운터 는 표준 라이브러리의 강력한 도구로이를 위해 완벽 할 수 있습니다. 카운터가있는 다른 솔루션이 하나뿐입니다. 그러나 해당 솔루션은 해시 가능 키로 제한됩니다 .

Counter에서 해시 할 수없는 키를 허용하기 위해 컨테이너 클래스를 만들었습니다 .Container 클래스는 객체의 기본 해시 함수를 가져 오려고 시도하지만 실패하면 ID 함수를 시도합니다. 또한 eq해시 메소드를 정의합니다 . 솔루션에서 해시 할 수 없는 항목 을 허용하기에 충분해야합니다 . 해싱 불가능한 객체는 마치 해싱 가능한 것처럼 처리됩니다. 그러나이 해시 함수는 해싱 할 수없는 객체에 ID를 사용하므로 해시 할 수없는 두 개의 동일한 객체가 작동하지 않습니다. 이것을 재정의하고 동등한 가변 유형의 해시를 사용하도록 변경하는 것이 좋습니다 ( hash(tuple(my_list))if my_listis a list 사용).

나는 또한 두 가지 해결책을 만들었다. 'OrderedCounter'라는 OrderedDict 및 Counter의 하위 클래스를 사용하여 항목의 순서를 유지하는 또 다른 솔루션입니다. 이제 기능은 다음과 같습니다.

from collections import OrderedDict, Counter

class Container:
    def __init__(self, obj):
        self.obj = obj
    def __eq__(self, obj):
        return self.obj == obj
    def __hash__(self):
        try:
            return hash(self.obj)
        except:
            return id(self.obj)

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

def remd(sequence):
    cnt = Counter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

def oremd(sequence):
    cnt = OrderedCounter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

remd는 정렬되지 않은 정렬이고, oremd는 정렬 된 정렬입니다. 어느 쪽이 더 빠른지 명확하게 알 수 있지만 어쨌든 설명하겠습니다. 비 정렬 정렬은 약간 빠릅니다. 주문이 필요 없기 때문에 적은 데이터를 유지합니다.

이제 각 답변의 속도 비교를 보여주고 싶었습니다. 그래서 지금 할게요.

어떤 기능이 가장 빠릅니까?

중복을 제거하기 위해 몇 가지 답변에서 10 가지 기능을 수집했습니다. 각 함수의 속도를 계산하고 matplotlib.pyplot을 사용하여 그래프에 넣었습니다 .

나는 이것을 3 라운드의 그래프로 나누었다. 해시 가능은 해시 할 수있는 객체이고, 해시 불가능은 해시 할 수없는 객체입니다. 정렬 된 순서는 순서를 유지하는 순서이며, 정렬되지 않은 순서는 순서를 유지하지 않습니다. 이제 몇 가지 용어가 더 있습니다.

정렬되지 않은 Hashable 은 중복을 제거하는 모든 방법에 대한 것이 었으며 반드시 주문을 유지할 필요는 없었습니다. unhashables를 위해 작동 할 필요는 없었지만 가능했습니다.

Ordered Hashable 은 목록에있는 항목의 순서를 유지하는 모든 방법에 대한 것이었지만 해싱 할 수없는 경우에는 작동하지 않았지만 가능했습니다.

Ordered Unhashable 은 목록의 항목 순서를 유지하고 해싱 불가능한 작업을 수행하는 방법이었습니다.

y 축에서 걸리는 시간은 초입니다.

x 축에는 함수가 적용된 숫자가 있습니다.

우리는 다음과 같은 이해를 통해 정렬되지 않은 해시 가능 및 정렬 된 해시 가능에 대한 시퀀스를 생성했습니다. [list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]

해시 불가능한 주문의 경우 : [[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]

그렇지 않으면 10 배가 걸렸기 때문에 범위에 '단계'가 있습니다. 또한 개인적인 견해로는 읽기가 조금 더 쉬울 것으로 생각했습니다.

또한 범례의 핵심은 내가 기능의 가장 중요한 부분으로 추측하려고 한 것입니다. 어떤 기능이 최악 또는 최상입니까? 그래프 자체를 말합니다.

그 설정으로, 여기 그래프가 있습니다.

정렬되지 않은 해시

여기에 이미지 설명을 입력하십시오 (확대) 여기에 이미지 설명을 입력하십시오

주문 된 해시 블

여기에 이미지 설명을 입력하십시오 (확대) 여기에 이미지 설명을 입력하십시오

주문 된 해시 불가능

여기에 이미지 설명을 입력하십시오 (확대) 여기에 이미지 설명을 입력하십시오


11

내 목록에 dict가 있었으므로 위의 접근 방식을 사용할 수 없었습니다. 오류가 발생했습니다.

TypeError: unhashable type:

따라서 주문에 관심이 있거나 일부 항목은 해싱 할 수 없습니다 . 그런 다음이 유용한 것을 찾을 수 있습니다 :

def make_unique(original_list):
    unique_list = []
    [unique_list.append(obj) for obj in original_list if obj not in unique_list]
    return unique_list

어떤 사람들은 부작용이있는 목록 이해력이 좋은 해결책이 아니라고 생각할 수 있습니다. 대안은 다음과 같습니다.

def make_unique(original_list):
    unique_list = []
    map(lambda x: unique_list.append(x) if (x not in unique_list) else False, original_list)
    return unique_list

6
map부작용이있는 목록은 부작용이있는 listcomp보다 훨씬 잘못됩니다. 또한, lambda x: unique_list.append(x)어리 석고 느린 방법 unique_list.append입니다.
abarnert

한 줄에 요소를 추가하는 매우 유용한 방법입니다. 감사합니다!
ZLNK

2
@ZLNK 제발, 절대 사용하지 마십시오. 개념적으로 추악한 것 외에도 잠재적으로 큰 목록을 작성하고 기본 반복을 수행하기 위해 버릴 수 있기 때문에 매우 비효율적입니다.
Eli Korvigo 2014 년

10

지금까지 내가 본 모든 순서 보존 접근 방식은 순진한 비교 (O (n ^ 2) 시간 복잡성을 최대한 활용 함) 또는 해시 가능한 입력으로 제한된 중량 OrderedDicts/ set+ list조합을 사용합니다. 해시 독립적 인 O (nlogn) 솔루션은 다음과 같습니다.

업데이트 에는 key인수, 설명서 및 Python 3 호환성이 추가되었습니다 .

# from functools import reduce <-- add this import on Python 3

def uniq(iterable, key=lambda x: x):
    """
    Remove duplicates from an iterable. Preserves order. 
    :type iterable: Iterable[Ord => A]
    :param iterable: an iterable of objects of any orderable type
    :type key: Callable[A] -> (Ord => B)
    :param key: optional argument; by default an item (A) is discarded 
    if another item (B), such that A == B, has already been encountered and taken. 
    If you provide a key, this condition changes to key(A) == key(B); the callable 
    must return orderable objects.
    """
    # Enumerate the list to restore order lately; reduce the sorted list; restore order
    def append_unique(acc, item):
        return acc if key(acc[-1][1]) == key(item[1]) else acc.append(item) or acc 
    srt_enum = sorted(enumerate(iterable), key=lambda item: key(item[1]))
    return [item[1] for item in sorted(reduce(append_unique, srt_enum, [srt_enum[0]]))] 

그러나이 솔루션에는 주문 가능한 요소가 필요합니다. 나는 그것을 목록의 uniquify를 사용하여 사용할 것이다 : 그것은 tuple()목록과 해시를 고통스럽게 한다. | | | | -일반적으로 해시 프로세스는 전체 데이터의 크기에 비례하는 시간이 걸리는 반면이 솔루션은 목록의 길이에 따라 O (nlog (n)) 시간이 걸립니다.
loxaxs

집합 기반 접근 방식은 고유 항목의 정렬 + 탐지보다 똑같이 저렴하거나 (O (n log n)) 저렴하다고 생각합니다. (이 방법은 병렬 처리가 훨씬 나아질 것입니다.) 또한 초기 순서를 정확하게 유지하지는 않지만 예측 가능한 순서를 제공합니다.
9000

@ 9000 사실입니다. 나는 해시 테이블 기반 접근법의 시간 복잡성을 언급 한 적이 없으며, 분명히 O (n)입니다. 여기서 해시 테이블을 통합 한 많은 답변을 찾을 수 있습니다. 그러나 객체는 해시 가능해야하기 때문에 보편적이지 않습니다. 또한 메모리를 많이 사용합니다.
Eli Korvigo

이 답변을 읽고 이해하는 데 시간이 걸립니다. 지수를 사용하지 않을 때 열거 할만한 요점이 있습니까? reduce() 이미 정렬 된 컬렉션 노력 srt_enum이 적용 않았는지, sorted다시?
Brayoni

@Brayoni 첫 번째 정렬은 동일한 값을 그룹화하고 두 번째 정렬은 초기 순서를 복원하는 것입니다. 원래의 상대 순서를 추적하려면 열거가 필요합니다.
엘리 코르 비고

9

순서를 유지하고 외부 모듈을 사용하지 않으려면 다음과 같이하십시오.

>>> t = [1, 9, 2, 3, 4, 5, 3, 6, 7, 5, 8, 9]
>>> list(dict.fromkeys(t))
[1, 9, 2, 3, 4, 5, 6, 7, 8]

참고 :이 방법은 모양의 순서를 유지하므로 위에서 볼 수 있듯이 9 개는 처음 등장했기 때문에 1 개가옵니다. 그러나 이것은 당신이 할 때와 같은 결과입니다

from collections import OrderedDict
ulist=list(OrderedDict.fromkeys(l))

그러나 훨씬 짧고 빠르게 실행됩니다.

fromkeys함수가 새 키를 만들려고 할 때마다 값이 이미 존재하면 단순히 덮어 쓰므로 작동합니다. 그러나 fromkeys모든 키가 값을 갖는 사전 을 작성하기 때문에 사전에 전혀 영향을 미치지 않습니다.None 방법으로 모든 중복을 효과적으로 제거합니다.



8

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

>>> t = [1, 2, 3, 3, 2, 4, 5, 6]
>>> s = [x for i, x in enumerate(t) if i == t.index(x)]
>>> s
[1, 2, 3, 4, 5, 6]

위의 작동 방식은 index요소의 첫 번째 인덱스 만 반환하기 때문입니다 . 중복 요소의 지수가 더 높습니다. 여기를 참조 하십시오 :

list.index (x [, start [, end]])
값이 x 인 첫 번째 항목의 목록에서 0 부터 시작 하는 인덱스를 반환합니다. 그러한 항목이 없으면 ValueError를 발생시킵니다.


이것은 엄청나게 비효율적입니다. list.index선형 시간 연산이므로 솔루션을 2 차로 만듭니다.
Eli Korvigo

네가 옳아. 그러나 또한 솔루션이 순서를 유지하는 하나의 라이너가되도록 의도 된 것이 분명합니다. 다른 모든 것은 이미 여기에 있습니다.
Atonal

7

세트를 사용해보십시오.

import sets
t = sets.Set(['a', 'b', 'c', 'd'])
t1 = sets.Set(['a', 'b', 'c'])

print t | t1
print t - t1

7

순서 유지로 변형을 줄입니다.

우리가 목록을 가지고 있다고 가정하십시오 :

l = [5, 6, 6, 1, 1, 2, 2, 3, 4]

변형 줄이기 (비효율적) :

>>> reduce(lambda r, v: v in r and r or r + [v], l, [])
[5, 6, 1, 2, 3, 4]

5 배 더 빠르지 만 더 정교함

>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]

설명:

default = (list(), set())
# user list to keep order
# use set to make lookup faster

def reducer(result, item):
    if item not in result[1]:
        result[0].append(item)
        result[1].add(item)
    return result

reduce(reducer, l, default)[0]

7

목록에서 중복을 제거하는 가장 좋은 방법은 파이썬에서 사용 가능한 set () 함수를 사용하여 해당 세트를 목록으로 다시 변환하는 것 입니다.

In [2]: some_list = ['a','a','v','v','v','c','c','d']
In [3]: list(set(some_list))
Out[3]: ['a', 'c', 'd', 'v']

@MeetZaveri 다행입니다.!
Anurag Misra

새로운 목록과 세트를 인스턴스화하는 것은 무료가 아닙니다. 빠른 연속으로 (즉, 매우 엄격한 루프에서) 여러 번이 작업을 수행하고 목록이 매우 작 으면 어떻게됩니까?
Z4-tier

6

다음 기능을 사용할 수 있습니다 :

def rem_dupes(dup_list): 
    yooneeks = [] 
    for elem in dup_list: 
        if elem not in yooneeks: 
            yooneeks.append(elem) 
    return yooneeks

:

my_list = ['this','is','a','list','with','dupicates','in', 'the', 'list']

용법:

rem_dupes(my_list)

[ 'this', 'is', 'a', 'list', 'with', 'dupicates', 'in', 'the']


5

이 작업을 수행하는 다른 방법을 제안하는 다른 많은 답변이 있지만 모두 일괄 작업이며 일부는 원래 순서를 버립니다. 필요한 것에 따라 괜찮을 수도 있지만 각 값의 첫 번째 인스턴스 순서대로 값을 반복하고 즉시 복제본을 제거하려는 경우 한 번에 모두 사용할 수 있습니다 이 발전기 :

def uniqify(iterable):
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item

그러면 생성기 / 반복기가 반환되므로 반복기를 사용할 수있는 곳이면 어디서나 사용할 수 있습니다.

for unique_item in uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]):
    print(unique_item, end=' ')

print()

산출:

1 2 3 4 5 6 7 8

당신이 원하는 경우 list, 당신은 이것을 할 수 있습니다 :

unique_list = list(uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]))

print(unique_list)

산출:

[1, 2, 3, 4, 5, 6, 7, 8]

seen = set(iterable); for item in seen: yield item거의 확실히 빠릅니다. (나는이 특정 경우를 시도하지 않았지만 그건 내 추측 일 것입니다.)
dylnmc

2
@dylnmc, 그것은 일괄 작업이며 주문도 잃습니다. 내 대답은 구체적으로 즉석에서 처음으로 발생하도록 의도되었습니다. :)
Cyphase

5

세트를 사용하지 않고

data=[1, 2, 3, 1, 2, 5, 6, 7, 8]
uni_data=[]
for dat in data:
    if dat not in uni_data:
        uni_data.append(dat)

print(uni_data) 

5

set중복을 제거 하는 데 사용할 수 있습니다 .

mylist = list(set(mylist))

그러나 결과는 정렬되지 않습니다. 그것이 문제라면 :

mylist.sort()

1
당신은 할 수 있습니다 : mylist = sorted (list (set (mylist)))
Erik Campobadal

5

더 좋은 방법은

import pandas as pd

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanList = pd.Series(myList).drop_duplicates().tolist()
print(cleanList)

#> [1, 2, 3, 5, 6, 7, 8]

순서는 그대로 유지됩니다.


이것이 잘 작동 할 수도 있지만, 이 목적으로 팬더 와 같은 무거운 라이브러리를 사용하는 것은 과도한 것처럼 보입니다.
Glutexo

4

이것은 너무 번거 로움이없는 주문 (OrderdDict 및 기타)에 관심이 있습니다. 아마도 가장 파이썬적인 방법이나 가장 짧은 방법은 아니지만 트릭을 수행합니다.

def remove_duplicates(list):
    ''' Removes duplicate items from a list '''
    singles_list = []
    for element in list:
        if element not in singles_list:
            singles_list.append(element)
    return singles_list

1. 내장 이름을 가리지 않아야합니다 (적어도만큼 중요 list). 2. 귀하의 방법은 매우 나쁘게 확장됩니다 :의 요소 개수가 2 차입니다 list.
Eli Korvigo

1. 맞습니다. 그러나 이것은 예입니다. 2. 맞습니다. 그것이 바로 제가 제공 한 이유입니다. 여기에 게시 된 모든 솔루션에는 장단점이 있습니다. 일부 희생 단순성 또는 순서, 내 희생 확장 성.
cgf

이것은 "페인트 화가"알고리즘입니다.
Z4-tier

4

아래 코드는 목록에서 중복을 제거하는 데 간단합니다.

def remove_duplicates(x):
    a = []
    for i in x:
        if i not in a:
            a.append(i)
    return a

print remove_duplicates([1,2,2,3,3,4])

[1,2,3,4]를 반환합니다


2
주문에 신경 쓰지 않으면 시간이 훨씬 오래 걸립니다. list(set(..))(1 백만 패스 이상)이 솔루션을 약 10 초 동안 이길 것입니다.이 방법은 약 12 ​​초가 list(set(..))걸리고 약 2 초만 걸립니다!
dylnmc

@dylnmc 이것은 또한 상당히 오래된 답변
Eli Korvigo

4

다음은 답글에 나열된 다른 사람들과 결합하는 가장 빠른 파이 토닉 솔루션입니다.

단락 평가의 구현 세부 사항을 사용하면 충분히 빠른 목록 이해를 사용할 수 있습니다. visited.add(item)항상 None결과로 반환 됩니다. 결과는로 평가 False되므로or 은 항상 이러한 표현식의 결과입니다.

스스로 시간

def deduplicate(sequence):
    visited = set()
    adder = visited.add  # get rid of qualification overhead
    out = [adder(item) or item for item in sequence if item not in visited]
    return out

4

set 사용 :

a = [0,1,2,3,4,3,3,4]
a = list(set(a))
print a

고유 한 사용 :

import numpy as np
a = [0,1,2,3,4,3,3,4]
a = np.unique(a).tolist()
print a

4

운수 나쁘게. 여기에있는 대부분의 답변은 순서를 유지하지 못하거나 너무 깁니다. 다음은 간단한 순서 유지 답변입니다.

s = [1,2,3,4,5,2,5,6,7,1,3,9,3,5]
x=[]

[x.append(i) for i in s if i not in x]
print(x)

이렇게하면 중복이 제거되었지만 순서는 유지하면서 x가 표시됩니다.


3

파이썬 3에서 매우 간단한 방법 :

>>> n = [1, 2, 3, 4, 1, 1]
>>> n
[1, 2, 3, 4, 1, 1]
>>> m = sorted(list(set(n)))
>>> m
[1, 2, 3, 4]

2
sorted(list(...))중복 됨 ( sorted이미 인수를 이미 암시 적으로 new로 변환하고 list정렬 한 다음 new를 반환 list하므로 두 가지를 모두 사용하여 불필요한 임시 작성 list) list결과를 정렬 할 필요가없는 경우 에만 사용 하고 결과를 정렬해야하는 경우 에만 사용하십시오 sorted.
ShadowRanger

3

파이썬의 마술 내장형

파이썬에서는 이와 같은 복잡한 경우를 파이썬의 내장 유형으로 만 처리하는 것이 매우 쉽습니다.

어떻게하는지 보여 드리겠습니다!

방법 1 : 일반적인 경우

목록에서 중복 된 요소를 제거하고 정렬 순서를 유지하는 방법 ( 1 줄 코드 )

line = [1, 2, 3, 1, 2, 5, 6, 7, 8]
new_line = sorted(set(line), key=line.index) # remove duplicated element
print(new_line)

당신은 결과를 얻을 것이다

[1, 2, 3, 5, 6, 7, 8]

방법 2 : 특별한 경우

TypeError: unhashable type: 'list'

해싱 불가능을 처리하는 특별한 경우 ( 3 라인 코드 )

line=[['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']]

tuple_line = [tuple(pt) for pt in line] # convert list of list into list of tuple
tuple_new_line = sorted(set(tuple_line),key=tuple_line.index) # remove duplicated element
new_line = [list(t) for t in tuple_new_line] # convert list of tuple into list of list

print (new_line)

당신은 결과를 얻을 것이다 :

[
  ['16.4966155686595', '-27.59776154691', '52.3786295521147'], 
  ['17.6508629295574', '-27.143305738671', '47.534955022564'], 
  ['18.8051102904552', '-26.688849930432', '42.6912804930134'], 
  ['19.5504702331098', '-26.205884452727', '37.7709192714727'], 
  ['20.2929416861422', '-25.722717575124', '32.8500163147157']
]

튜플은 해시 가능하고 목록과 튜플간에 데이터를 쉽게 변환 할 수 있기 때문에

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.