2013-08-01 12 views
0

목록에서 같은 항목을 제거하려면 어떻게합니까? 말Python 목록에서 항목 제거

A = [1, 2, 3, 8, 7, 8, 8, 7, 6]

그리고 8 어떻게 이런 짓을해야 모두 제거하려면? 번호가 맞지 않으면?

+0

당신은 목록에 사용할 데이터 구조입니다 확실 해요? 당신은 당신의 코드가 무엇을하고 있는지에 따라, 또는 아마도 dict 또는 뭔가를 원할 수 있습니다. – user2357112

+0

@ user2357112 : 그가 8 살 중 전체를 없애기를 원한다는 것을 고려하면, 1 살을 제외하고는 모두를 제거하기를 원한다. 그리고 그는 여전히 7 권을 갖고 싶어한다. 원래의 순서대로 ... 나는 그가 원하는 것을 생각하지 않는다. 하지만 당신이 옳은 것, 그가하려고하는 것에 대한 더 많은 정보없이 그가 원하는 것을 말할 수는 없습니다. – abarnert

+0

원래 주문을 유지해야하는지 또는 중복이 중요한지 여부는 알 수 없습니다. 중요한 것은 구조체에 구조체가 8 개가 있는지 여부와 관계없이 세트는 "세트에 8이 들어 있습니다"와 "세트에 8이 없습니다"사이의 빠른 전환을 허용 할뿐만 아니라 "세트에 8이 들어 있는지"확인. – user2357112

답변

0
In [13]: A= [1, 2, 3, 8, 7, 8, 8, 7, 6] 

In [14]: [i for i in A if i!=8] 
Out[14]: [1, 2, 3, 7, 7, 6] 

In [15]: filter(lambda i: i!=8, A) 
Out[15]: [1, 2, 3, 7, 7, 6] 
+3

'filter'를 사용한다면'λ (람다) '대신'(8) .__ ne__' 또는'partial (ne, 8)'을 그냥 넘길 수 있습니다. 나는 그것이 더 읽을 수 있는지 확실하지 않지만 확실하게 더 기능적이다. :) – abarnert

+0

내가 잊어 버린 한 가지 빠른 메모 : 2.x에서는 'int .__ ne__'이 없습니다. 그것은'int .__ cmp__'에 대한 후퇴에 의존합니다. 그러나'int .__ cmp__'는 사실 일 수 있습니다. 왜냐하면 조금 해킹되지만, 여기에서는'(8) .__ cmp__'를 사용할 수 있습니다. 그러나 빠른 테스트에서 람다 또는 부분적인 것보다 훨씬 빠르지 않으며 구식 비교를 이해해야하는 추가 가독성 비용은 3.x보다 훨씬 나쁜 아이디어라고 생각합니다. – abarnert

4

쉬운 방법은 8이 아닌 항목이 모두 포함 된 새 목록을 만드는 것입니다. 예를 들면 :

B=[item for item in A if item != 8] 

당신이 정말로 다른 객체가 A와 같은 목록에 대한 참조를 가지고 있기 때문에 (예를 들어, 자리에서 그것을 할 경우, 다른 객체가 볼 것을 당신이 원하는 변경) 할 수 있습니다. 그러나 그것은 더 까다 롭습니다. 예를 들어, 인덱스로 삭제하면 어떤 지점에서 뒤로 이동해야합니다. 첫 번째 8을 제거하면 나중에 모두8에 새 인덱스가 있으므로 항상 마지막 인덱스를 먼저 삭제해야합니다. :

indices = [index for index, item in enumerate(A) if item==8] 
for index in reversed(indices): 
    del A[index] 

아니면이 실패 할 때까지 당신은 remove에 계속 시도 할 수 있지만,이 못생긴 모두 느린 :

while True: 
    try: 
     A.remove(8) 
    except ValueError: 
     break 

사실, 당신은 새를 만드는 여전히 오프 종종 더 낫다 목록을 작성한 다음 변경 사항 만 그 새 목록의 사본에 A를 NG :

A[:]=[item for item in A if item != 8] 

을 성능 테스트를 위해, 나는 제안 된 모든 답변에 대한 this code을 달렸다. 테스트 된 알고리즘은 다음과 같습니다.

  • revind :이 대답의 첫 번째 내부 알고리즘.
  • whiledel : Chris Barker의 첫 번째 알고리즘.
  • 슬라이드 : Chris Barker의 두 번째 알고리즘 (고정 버전).
  • genexpr : 나의 마지막 알고리즘이지만 listcomp 대신 genexpr을 사용합니다.
  • listcomp : 내 마지막 알고리즘.
  • filterer : 마지막 알고리즘이지만 필터를 사용합니다 (이것은 2.x에서는 목록을 만들지 만 3.x에서는 genexpr과 같은 반복기를 만듭니다).

A을 실제로 돌연변이 할 필요가 없으며 대개 그렇게하지 않으면 이름을 리바 인드 할 수 있습니다. 복사 후 돌연변이 알고리즘은 더욱 간단하고 빠릅니다. 그러나 나는 여기서 그것을 시험하지 않았다.

필자는 분명히 while while : error까지 제거 알고리즘을 테스트하지 않았으며, Chris Barker가 제안한 변형을 완벽하게 테스트하지 못했습니다. 왜냐하면 테스트 속도가 느려서 분명히 테스트 할 가치가 없기 때문입니다. 그러나 매우 빠른 테스트를 통해 변형은 예상대로 약 2 배 더 느리고 거의 모든 테스트 케이스에서 다른 것보다 느리게 진행됩니다.

어쨌든 테스트에서는 길이가 100K 또는 1M 인 무작위 목록에서 0을 제거하고 0에서 16,256 또는 65536 사이의 값을 사용합니다 (더 적은 고유 값, 제거 할 히트의 비율이 높음).

CPython을 사용하는 경우 listcomp 버전은 항상 가장 빠릅니다. 특히 N이 클 경우에는 가장 빠릅니다. PyPy를 사용할 때, N이 크고 M이 작을 때 내부 알고리즘이 이길 수 있지만,이 경우에는 모두입니다. 멋진 슬라이딩 알고리즘은 다른 내부 알고리즘의 2 차 동작을 제거하지만 간단한 경우에는 속도가 느려지므로 확실한 승자가없는 곳은 없습니다. (이것은 또한 가장 복잡한 것입니다. 아마도 첫 번째 시도에서 옳지 않았던 유일한 이유 일 것입니다.) 당신이 절대적으로 확신 할 만하다면, 당신은 아주 적은 수의 사본을 제거 할 것입니다. PyPy를 사용하고 Whiledel 솔루션을 고려하십시오. 다른 유스 케이스에서 또는 유스 케이스를 확실히 모르는 경우 listcomp를 사용합니다.

  64-bit python CPython 3.3.0 
      16 values 256 values 65536 values 
      100K 1000K 100K 1000K 100K 1000K 
revind 0.188 17.3 0.085 1.23 0.074 0.080 
whiledel 0.324 19.3 0.206 1.36 0.199 0.203 
slide  0.091 0.54 0.097 0.54 0.095 0.538 
genepxr 0.094 0.11 0.100 0.11 0.099 0.108 
listcomp 0.070 0.08 0.073 0.08 0.071 0.079 
filterer 0.081 0.09 0.080 0.09 0.835 0.088 

      64-bit python CPython 2.7.2 
      16 values 256 values 65536 values 
      100K 1000K 100K 1000K 100K 1000K 
revind 0.198 17.1 0.089 1.23 0.088 0.955 
whiledel 0.345 19.8 0.233 1.36 0.234 0.243 
slide  0.095 0.54 0.099 0.55 0.095 0.551 
genepxr 0.092 0.11 0.097 0.11 0.107 0.116 
listcomp 0.091 0.09 0.099 0.08 0.105 0.114 
filterer 0.122 0.23 0.132 0.09 0.135 0.150 

      64-bit python PyPy 1.9.0 (Python 2.7.2) 
      16 values 256 values 65536 values 
      100K 1000K 100K 1000K 100K 1000K 
revind 0.266 28.5 0.027 1.97 0.018 0.013 
whiledel 0.281 30.2 0.023 1.94 0.034 0.009 
slide  0.022 0.39 0.015 0.022 0.006 0.018 
genepxr 0.089 0.13 0.087 0.154 0.089 0.147 
listcomp 0.052 0.08 0.057 0.073 0.052 0.073 
filterer 0.054 0.07 0.053 0.078 0.048 0.074 

슬라이드의 성능을 예측하는 데 다소 어려움이 있습니다. 큰 N/small-M 경우에, 당신은 whiledel을 날려 버릴 것이라고 기대할 수는 있지만 실제로는 더 느립니다. 그러나 당신이 그것에 대해 생각한다면 : 그 알고리즘은 M 개의 선형 움직임을 N 개의 간단한 복사본으로 효과적으로 대체합니다. 전작은 O (NM)이고 후자는 O (N)이지만 C에서 루핑하는 승수 (또는 더 나은 것은 memmove)는 파이썬에서 루핑하는 것보다 훨씬 작기 때문에 무시할 수 없습니다. N/M은 엄청납니다 (모든 솔루션이 너무 빠르기 때문에 거의 중요하지 않습니다). 따라서 M 파이썬 루프와 NM C 루프를 수행하면 N 파이썬 루프를 쉽게 수행 할 수 있습니다.

+1

아니면'A : 8 in A : A.remove (8)'을 할 수 있습니다. –

+0

@ChrisBarker : True. 이것은'for' 루프보다 두 배 느리지 만, 선형 대신에 2 차 성능이 충분하다고 이미 결정했다면, 시간을 두 배로 늘리는 것에 신경을 쓴다고 생각합니다. 좋은 지적입니다. – abarnert

+0

좋은 지적 - 제자리에서의 선형 시간 솔루션에 대한 내 대답을보십시오. –

2

대부분의 답변은 데이터 복사를 제안합니다. 목록이 충분히 클 경우에는 바람직하지 않을 수 있습니다.

while 8 in A: A.remove(8) 

을 사용하면 쉽게 데이터를 복사 할 수 있습니다. 그러나 이것은 2 차 시간에 실행되며 목록이 클 경우에는 바람직하지 않습니다. 사용 선형 시간에 임의의 데이터를 복사하지 않고 그것을 할 :

def remove_all_from_list(L, n): 
    i = 0 
    while i < len(L): 
     if L[i] == n: 
      del L[i] # Do not increment i here, because L[i] will change 
     else: 
      i += 1 

>>> A = [1, 2, 3, 8, 7, 8, 8, 7, 6] 
>>> remove_all_from_list(A, 8) 
>>> A 
[1, 2, 3, 7, 7, 6] 

편집 : @abarnert 생각 나게 델 L [I]가 O가 (N) 그래서이 실제로 이차된다. 다음은이 말에 모든 셔플을 할 것입니다 현재 위치에서 솔루션 오 (N)에서 또 다른 시도 ...

def remove_all_from_list(L, n): 
    # indices takes *worse-case* O(N) space, but typical-case much less 
    indices = [i for i, x in enumerate(L) if x==n] 
    indices_seen = 0 
    num_indices = len(indices) 
    for i in xrange(len(L)): 
     while (indices_seen < num_indices and 
      i + indices_seen == indices[indices_seen]): 
      indices_seen += 1 
     if i+indices_seen >= len(L): 
      break 
     L[i] = L[i+indices_seen]    
    L[-indices_seen:] = [] 

, 그래서 각 요소에서 대부분 한 번에 이동됩니다. 나는 이것이 abarnert의 복제 방법만큼이나 많은 시간이 걸릴 것이라는 것을 알고 있습니다. 난 당신이 매우 큰 목록을 가지고있는 경우 메모리 사용을 줄이는 방법을 생각하려고 노력하고 있습니다.


최종 편집 : (의 @ ​​abarnert만큼 포괄적이지) 속도 테스트

import random 
L = range(30)*10000 
random.shuffle(L) 
from copy import copy 

for fn in [remove_1, remove_2, remove_3, remove_4, remove_5, remove_6]: 
    print fn.__name__ 
    %timeit fn(copy(L), 8) 

remove_1 # listcomp 
10 loops, best of 3: 39.1 ms per loop 
remove_2 # revind 
1 loops, best of 3: 1.7 s per loop 
remove_3 # try: remove; except: break 
1 loops, best of 3: 65.7 s per loop 
remove_4 # while n in L: L.remove(n) 
1 loops, best of 3: 129 s per loop 
remove_5 # whiledel 
1 loops, best of 3: 1.87 s per loop 
remove_6 # slide 
1 loops, best of 3: 227 ms per loop 
+0

목록의 나머지 부분은 선형 시간이 걸리기 때문에 (목록의 나머지 부분은 뒤섞여 야하기 때문에) 실제로는 선형 시간이 아닙니다. 그것을 무시하더라도, 시간을 내면 역 색인 생성 솔루션보다 실제로 속도가 느리고, 동일한 C 코드에서도 마찬가지입니다. 어쨌든, 복사 솔루션은 선형이며, 부팅하는 것이 훨씬 간단합니다. – abarnert

+0

답을 업데이트했습니다. 인덱스 목록을 작성하고 있으므로 O (N) 공간까지 사용할 수 있다는 것을 알지만 일반적인 경우에는 적습니다. 나는 여기서 추가적인 복잡성을 놓치고 있다고 생각하지 않는다. 생각? –

+0

새 버전이 실제로 올바르지 않습니다. 무엇이 잘못되었는지 디버깅하지는 않았지만, 첫 번째 알고리즘과 모든 알고리즘을 목록과 비교하여 실행하면 93578 값이 남았고, 두 번째 알고리즘은 99992로 끝납니다. 또한, 복사본을 만들고'L [:]'을 대다수, 아마도 모든 경우 ...로 바꾸십시오. 그러나 응답하기 전에 완전한 테스트 결과가 나올 때까지 기다려주십시오. – abarnert