문제

이 있는 내장을 제거하는 중복에서 목록에서 Python,하는 동안 보존하기 위해?고 있다는것을 알 수 있습니다 설정을 사용할 수 있습하고 중복을 제거하거나 파괴하는 원래의 순서입니다.나도 알 수 있는 롤 내 자신 다음과 같다:

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

(감사 샘플 코드.)

그러나 나는 같은 소용이 자신의 내장 또는 보 Pythonic 관용 가능한 경우.

관련 질문: Python,무엇이 가장 빠른 알고리즘을 제거를 위한 중복 목록에서 모든 요소가 고유의 하는 동안 보존 순서?

도움이 되었습니까?

해결책

여기에는 몇 가지 대안이 있습니다. 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/

영형(1) 작업 당 삽입, 삭제 및 멤버 확인.

(작은 추가 참고 : seen.add() 항상 돌아옵니다 None, 그래서 or 위의 논리 테스트의 필수 부분이 아닌 세트 업데이트를 시도하는 방법으로 만 있습니다.)

다른 팁

2016 년 편집

레이몬드로 지적했다, 파이썬 3.5+에서 OrderedDict C에서 구현되며, 목록 이해 접근법은 OrderedDict (실제로 끝에 목록이 필요하지 않으면 입력이 매우 짧은 경우에만). 따라서 3.5+를위한 최상의 솔루션은입니다 OrderedDict.

중요한 편집 2015

처럼 @Abarnert 참고, more_itertools 도서관 (pip install more_itertools)는 a를 포함합니다 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:

실행 시간: 켜짐)

>>> 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 항상 반환되는 내부 방법입니다 None 그래서 not None 평가합니다 True.

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

파이썬 2.7, 원래 순서로 유지하면서 반복적 인 중복을 제거하는 새로운 방법은 다음과 같습니다.

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

파이썬에서 3.5, OrderedDict에는 C 구현이 있습니다. 내 타이밍은 이것이 이제 Python 3.5의 다양한 접근 방식 중 가장 빠르고 가장 짧다는 것을 보여줍니다.

파이썬에서 3.6, 정기적 인 덕트는 질서와 소형이되었습니다. (이 기능은 CPYTHON 및 PYPY 용으로 보유하지만 다른 구현에는 존재하지 않을 수 있습니다). 그것은 우리에게 주문을 유지하면서 가장 빠른 전조 방법을 제공합니다.

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

파이썬에서 3.7, 정기적 인 DITT는 모든 구현에 걸쳐 주문 된 두 가지를 보장합니다. 따라서 가장 짧고 빠른 솔루션은 다음과 같습니다.

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

@max에 대한 응답 : 일단 3.6 또는 3.7로 이동하고 대신 일반 딕을 사용하면 순서대로, 당신은 실제로 다른 방식으로 성능을 이길 수 없습니다. 사전은 밀도가 높고 오버 헤드가 거의없는 목록으로 쉽게 변환됩니다. 대상 목록은 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']

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

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

편집 : "순서 보존"은 목록이 실제로 주문되었음을 의미한다고 가정했습니다. 그렇지 않은 경우 Mizardx의 솔루션이 올바른 솔루션입니다.

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

죽은 말을 걷어차는 것이 아니라 (이 질문은 매우 오래되었고 이미 좋은 답변을 가지고 있습니다), 여기에는 많은 상황에서 매우 빠르고 사용하기가 간단한 팬더를 사용하는 솔루션이 있습니다.

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]

주문을 유지하고 싶다면

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

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 

그냥 다른 추가(매우 성능이)구현의 기능에서 외부 모듈1: iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen
>>> lst = [1,1,1,2,3,2,2,2,1,3,4]

>>> list(unique_everseen(lst))
[1, 2, 3, 4]

타이밍

나는 몇몇 타이밍(Python3.6)그리고 이러한 표시는 그것보다 더 빨리 모든 다른 대안은 나를 포함,테스트 OrderedDict.fromkeys, f7more_itertools.unique_everseen:

%matplotlib notebook

from iteration_utilities import unique_everseen
from collections import OrderedDict
from more_itertools import unique_everseen as mi_unique_everseen

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

def iteration_utilities_unique_everseen(seq):
    return list(unique_everseen(seq))

def more_itertools_unique_everseen(seq):
    return list(mi_unique_everseen(seq))

def odict(seq):
    return list(OrderedDict.fromkeys(seq))

from simple_benchmark import benchmark

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: list(range(2**i)) for i in range(1, 20)},
              'list size (no duplicates)')
b.plot()

enter image description here

과하는지 확인하고 내가 또한 테스트와 더 많은 중복을 확인하는 경우에 분명한 차이를 느낄 수 있습니다:

import random

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(1, 20)},
              'list size (lots of duplicates)')
b.plot()

enter image description here

하나만을 포함하는 하나의 값:

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: [1]*(2**i) for i in range(1, 20)},
              'list size (only duplicates)')
b.plot()

enter image description here

이러한 모든 경우에 있어서의 iteration_utilities.unique_everseen 함수가 가장 빠르(내 컴퓨터에서).


iteration_utilities.unique_everseen 함수를 처리할 수 있습 unhashable 에 값을 입력(단과 O(n*n) 성능을 대신 O(n) 성능한 값은 해쉬).

>>> lst = [{1}, {1}, {2}, {1}, {3}]

>>> list(unique_everseen(lst))
[{1}, {2}, {3}]

1 면책 조항:나의 저자는 패키지입니다.

을 위한 또 다른 매우 늦은 응답을 다른 매우 오래되는 질문:

itertools 조리법 는 기능을 갖추고 있는 이 사용하여, seen 정 기술지만,:

  • 을 처리하는 표준 key 기능입니다.
  • 을 사용하지 않기 흉하게 해킹.
  • 을 최적화하여 루프 pre-바인딩 seen.add 대 찾 N 다.(f7 또한 이지만,일부 버전하지 않습니다.)
  • 을 최적화하여 루프를 사용하여 ifilterfalse, 다,그래서 당신은 루프를 통해 독특한 요소 라이브러리는 모두 지원되며,대신 모든다.(당신은 여전히 통해 반복하는 모두의 안에 그들을 ifilterfalse, 물론,하지만 그에 따,그리고 훨씬 더 빠르다.)

그것은 실제로보다 더 빨리 f7?에 따라 데이터를,그래서 당신은 그것을 테스트하고 보십시오.당신이 원하는 목록의 끝에서, f7 사용 listcomp,그리고 방법은 없을 것을 여기에.(당신이 직접 할 수 있습니다 appendyielding,또는 당신를 공급할 수 있습으로 발전기 list 기능,하지만 어느 하나될 수 있으로 빠른 속도로 LIST_APPEND 내부 listcomp.) 어쨌든,일반적으로 짜내기서 몇 마이크로초 단위로 하지 않으로 중요한 데이 쉽게 이해할 수 있는 재사용할 수 있는,이미 작성된 함수는 필요하지 않 DSU 때 당신은 장식합니다.

로의 모든 요리법에,그것은 또한에서 사용 가능 more-iterools.

원하는 경우에는 없key 경우에,단순화할 수 있습으로 그것:

def unique(iterable):
    seen = set()
    seen_add = seen.add
    for element in itertools.ifilterfalse(seen.__contains__, iterable):
        seen_add(element)
        yield element

~ 안에 파이썬 3.7 그리고 위의 사전은입니다 보장 주요 삽입 순서를 기억합니다. 답변 이것 질문은 현재의 상황을 요약합니다.

그만큼 OrderedDict 따라서 해결책은 쓸모 없게되며 수입 명세서가 없으면 간단히 발행 할 수 있습니다.

>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> list(dict.fromkeys(lst))
[1, 2, 3, 4]

Mizardx를 기반으로 한 해시블 유형 (예 : 목록 목록)의 경우 :

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

Haskell을 정의하는 데 사용되는 재귀 아이디어를 빌리십시오 nub 목록의 기능은 재귀적인 접근법입니다.

def unique(lst):
    return [] if lst==[] else [lst[0]] + unique(filter(lambda x: x!= lst[0], lst[1:]))

예 :

In [118]: unique([1,5,1,1,4,3,4])
Out[118]: [1, 5, 4, 3]

나는 데이터 크기를 늘리기 위해 그것을 시도했고, 하위 선형 시간-복잡성을 보았습니다 (결정적이지는 않지만 이것이 정상 데이터에 적합하다는 것을 암시합니다).

In [122]: %timeit unique(np.random.randint(5, size=(1)))
10000 loops, best of 3: 25.3 us per loop

In [123]: %timeit unique(np.random.randint(5, size=(10)))
10000 loops, best of 3: 42.9 us per loop

In [124]: %timeit unique(np.random.randint(5, size=(100)))
10000 loops, best of 3: 132 us per loop

In [125]: %timeit unique(np.random.randint(5, size=(1000)))
1000 loops, best of 3: 1.05 ms per loop

In [126]: %timeit unique(np.random.randint(5, size=(10000)))
100 loops, best of 3: 11 ms per loop

또한 이것이 다른 작전에 의해 독창성에 쉽게 일반화 될 수 있다는 것이 흥미 롭다고 생각합니다. 이와 같이:

import operator
def unique(lst, cmp_op=operator.ne):
    return [] if lst==[] else [lst[0]] + unique(filter(lambda x: cmp_op(x, lst[0]), lst[1:]), cmp_op)

예를 들어, 반올림 개념을 동일한 정수로 사용하는 함수를 동일한 정수로 사용하는 기능을 전달할 수 있습니다.

def test_round(x,y):
    return round(x) != round(y)

그러면 고유 한 (some_list, test_round)는 독창성이 더 이상 전통적인 평등을 의미하지 않는 (이 문제에 대한 세트 기반 또는 딕 키 기반 접근법을 사용함으로써 암시 된) 목록의 고유 한 요소를 제공합니다. 요소가 둥글게 할 수있는 각각의 정수 k에 대해 k로 반올림하는 첫 번째 요소 만 (예 :

In [6]: unique([1.2, 5, 1.9, 1.1, 4.2, 3, 4.8], test_round)
Out[6]: [1.2, 5, 1.9, 4.2, 3]

5 x 더 빠르게 변형을 줄이지 만 더 정교합니다

>>> l = [5, 6, 6, 1, 1, 2, 2, 3, 4]
>>> 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())
# use 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]
[5, 6, 1, 2, 3, 4]

Mizardx의 답변은 여러 가지 접근 방식을 잘 모으고 있습니다.

이것은 내가 큰 소리로 생각하면서 내가 생각해 낸 것입니다.

mylist = [x for i,x in enumerate(mylist) if x not in mylist[i+1:]]

'_ [1]'기호에 의해 구축 될 때 목록 이해력을 참조 할 수 있습니다.
예를 들어, 다음 함수는 고유 한 요소 목록을 목록 이해력을 참조하여 주문을 변경하지 않고 요소 목록을 확인합니다.

def unique(my_list): 
    return [x for x in my_list if x not in locals()['_[1]']]

데모:

l1 = [1, 2, 3, 4, 1, 2, 3, 4, 5]
l2 = [x for x in l1 if x not in locals()['_[1]']]
print l2

산출:

[1, 2, 3, 4, 5]

당신은 일종의 못생긴 목록 이해력 해킹을 할 수 있습니다.

[l[i] for i in range(len(l)) if l.index(l[i]) == i]

비교적 효과적인 접근 _sorted_numpy 배열 :

b = np.array([1,3,3, 8, 12, 12,12])    
numpy.hstack([b[0], [x[0] for x in zip(b[1:], b[:-1]) if x[0]!=x[1]]])

출력 :

array([ 1,  3,  8, 12])
l = [1,2,2,3,3,...]
n = []
n.extend(ele for ele in l if ele not in set(n))

O (1)를 사용하는 생성기 표현식은 세트의 조회를하여 새 목록에 요소를 포함할지 여부를 결정합니다.

간단한 재귀 솔루션 :

def uniquefy_list(a):
    return uniquefy_list(a[1:]) if a[0] in a[1:] else [a[0]]+uniquefy_list(a[1:]) if len(a)>1 else [a[0]]

하나의 라이너가 필요하면 아마도 도움이 될 것입니다.

reduce(lambda x, y: x + y if y[0] not in x else x, map(lambda x: [x],lst))

... 내가 틀렸다면 일해야하지만 나를 바로 잡으십시오

일상적으로 사용하는 경우 pandas, 및 성능보다 미학이 선호되는 다음 내장 기능을 고려하십시오. pandas.Series.drop_duplicates:

    import pandas as pd
    import numpy as np

    uniquifier = lambda alist: pd.Series(alist).drop_duplicates().tolist()

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

    alist = np.random.randint(low=0, high=1000, size=10000).tolist()

    print uniquifier(alist) == f7(alist)  # True

타이밍:

    In [104]: %timeit f7(alist)
    1000 loops, best of 3: 1.3 ms per loop
    In [110]: %timeit uniquifier(alist)
    100 loops, best of 3: 4.39 ms per loop

이것은 질서를 보존하고 O (n) 시간으로 실행됩니다. 기본적으로 아이디어는 중복이있는 곳이면 어디든지 구멍을 만들고 바닥으로 내려가는 것입니다. 읽기 및 쓰기 포인터를 사용합니다. 중복이 발견 될 때마다 읽기 포인터 고급 만, 중복 된 항목에서 쓰기 포인터를 사용하여 덮어 쓰기.

def deduplicate(l):
    count = {}
    (read,write) = (0,0)
    while read < len(l):
        if l[read] in count:
            read += 1
            continue
        count[l[read]] = True
        l[write] = l[read]
        read += 1
        write += 1
    return l[0:write]

가져온 모듈 또는 세트를 사용하지 않고 솔루션 :

text = "ask not what your country can do for you ask what you can do for your country"
sentence = text.split(" ")
noduplicates = [(sentence[i]) for i in range (0,len(sentence)) if sentence[i] not in sentence[:i]]
print(noduplicates)

출력 제공 :

['ask', 'not', 'what', 'your', 'country', 'can', 'do', 'for', 'you']

현장 내 방법

이 방법은 목록의 모든 요소에 대한 목록에 대한 선형 조회를 가지고 있기 때문에 2 차입니다 (우리는 목록으로 인해 목록을 재정렬하는 비용을 추가해야합니다. del 에스).

즉, 목록의 끝에서 시작하여 왼쪽 하위 목록에있는 각 항을 제거하는 원점으로 진행하면 제자리에 작동 할 수 있습니다.

코드 의이 아이디어는 간단합니다

for i in range(len(l)-1,0,-1): 
    if l[i] in l[:i]: del l[i] 

구현의 간단한 테스트

In [91]: from random import randint, seed                                                                                            
In [92]: seed('20080808') ; l = [randint(1,6) for _ in range(12)] # Beijing Olympics                                                                 
In [93]: for i in range(len(l)-1,0,-1): 
    ...:     print(l) 
    ...:     print(i, l[i], l[:i], end='') 
    ...:     if l[i] in l[:i]: 
    ...:          print( ': remove', l[i]) 
    ...:          del l[i] 
    ...:     else: 
    ...:          print() 
    ...: print(l)
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5, 2]
11 2 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]
10 5 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4]: remove 5
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4]
9 4 [6, 5, 1, 4, 6, 1, 6, 2, 2]: remove 4
[6, 5, 1, 4, 6, 1, 6, 2, 2]
8 2 [6, 5, 1, 4, 6, 1, 6, 2]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2]
7 2 [6, 5, 1, 4, 6, 1, 6]
[6, 5, 1, 4, 6, 1, 6, 2]
6 6 [6, 5, 1, 4, 6, 1]: remove 6
[6, 5, 1, 4, 6, 1, 2]
5 1 [6, 5, 1, 4, 6]: remove 1
[6, 5, 1, 4, 6, 2]
4 6 [6, 5, 1, 4]: remove 6
[6, 5, 1, 4, 2]
3 4 [6, 5, 1]
[6, 5, 1, 4, 2]
2 1 [6, 5]
[6, 5, 1, 4, 2]
1 5 [6]
[6, 5, 1, 4, 2]

In [94]:                                                                                                                             

중복 값을 시퀀스로 제거하지만 나머지 항목의 순서를 유지하십시오. 범용 발전기 기능의 사용.

# for hashable sequence
def remove_duplicates(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)

a = [1, 5, 2, 1, 9, 1, 5, 10]
list(remove_duplicates(a))
# [1, 5, 2, 9, 10]



# for unhashable sequence
def remove_duplicates(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield item
            seen.add(val)

a = [ {'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
list(remove_duplicates(a, key=lambda d: (d['x'],d['y'])))
# [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]

ZMK의 접근 방식은 매우 빠르지 만 주문을 자연스럽게 유지하는 목록 이해력을 사용합니다. CASE 민감한 문자열에 적용하려면 쉽게 수정할 수 있습니다. 이것은 또한 원래 사례를 보존합니다.

def DelDupes(aseq) :
    seen = set()
    return [x for x in aseq if (x.lower() not in seen) and (not seen.add(x.lower()))]

밀접하게 관련된 기능은 다음과 같습니다.

def HasDupes(aseq) :
    s = set()
    return any(((x.lower() in s) or s.add(x.lower())) for x in aseq)

def GetDupes(aseq) :
    s = set()
    return set(x for x in aseq if ((x.lower() in s) or s.add(x.lower())))
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top