한 목록에서 발생하는 모든 요소를 ​​다른 목록에서 제거


365

하자 내가 두 개의 목록이 말을 l1하고 l2. in의 l1 - l2모든 요소를 ​​반환하는 을 수행하고 싶습니다 .l1l2

이 작업을 수행하는 순진한 루프 접근 방식을 생각할 수 있지만 실제로는 비효율적입니다. 이것을하는 pythonic하고 효과적인 방법은 무엇입니까?

예를 들어, 내가 가지고있는 경우 l1 = [1,2,6,8] and l2 = [2,3,5,8], l1 - l2반환해야[1,6]


12
팁 : PEP8에 따르면 소문자 "L"은 1과 너무 비슷해 보이기 때문에 사용해서는 안된다고합니다.
spelchekr

2
동의한다. 나는이 전체 질문과 사람들이 왜 11과 12를 계속 사용했는지 궁금해하는 대답을 읽었습니다. @spelchekr의 의견을 읽었을 때만 의미가있었습니다.
robline


@JimG. 데이터 프레임과리스트는 동일하지 않습니다.
활동 감소

답변:


491

파이썬에는 List Comprehensions 라는 언어 기능 이있어 이러한 종류의 작업을 매우 쉽게 수행 할 수 있습니다. 다음 문장은 원하는 것을 정확하게 수행하고 결과를 저장합니다 l3.

l3 = [x for x in l1 if x not in l2]

l3포함합니다 [1, 6].


8
매우 pythonic; 나는 그것을 좋아한다! 얼마나 효율적입니까?
fandom

2
나는 매우 효율적이라고 생각하며, 달성하려는 것에 대해 매우 읽기 쉽고 명확하게 얻을 수있는 이점이 있습니다. 효율성과 관련하여 흥미로운 블로그 게시물을 발견했습니다. blog.cdleary.com/2010/04/efficiency-of-list-comprehensions
Donut

6
@ fandom : 목록 이해 자체는 상당히 효율적이지만 (메모리에 요소를 복제하지 않으면 생성기 이해가 더 효율적 일 수 있지만) in연산자는 목록에서 그렇게 효율적이지 않습니다. in목록에서 O (n) 인 반면 in세트에서 O (1)입니다. 그러나 수천 가지 이상의 요소에 도달 할 때까지 차이를 느끼지 못할 것입니다.
Daniel Pryden

1
l3 = [x for x in l1 if x not in set(l2)]? 나는 set(l2)두 번 이상 부름 받을 것이라고 확신합니다 .
Danosaure

5
당신은 또한 설정 l2s = set(l2)하고 말할 수 l3 = [x for x in l1 if x not in l2s]있습니다. 약간 더 쉽다.
spelchekr

149

한 가지 방법은 세트를 사용하는 것입니다.

>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])

58
이도에서 중복 제거 l1원하지 않는 부작용이있을 수있다.
kindall

37
.. 그리고 요소 순서를 잃습니다 (주문이 중요한 경우).
Danosaure

3
나는이 시간과 수용 된 답변의 시간을 정하고 약 3의 요인으로 더 성능이 좋았다고 덧붙이고 싶습니다 timeit.timeit('a = [1,2,3,4]; b = [1,3]; c = [i for i in a if a not in b]', number=100000) -> 0.12061533199999985 timeit.timeit('a = {1,2,3,4}; b = {1,3}; c = a - b', number=100000) -> 0.04106225999998969. 따라서 성능이 중요한 요소라면이 답변이 더 적절할 수도 있습니다 (또한 중복 또는 순서에 관심이없는 경우)
wfgeo

37

대안으로 원하는 결과를 얻기 위해 람다 식과 함께 사용할filter 수도 있습니다. 예를 들면 다음과 같습니다.

>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])

#     v  `filter` returns the a iterator object. Here I'm type-casting 
#     v  it to `list` in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]

성능 비교

여기에 언급 된 모든 답변의 성능을 비교하고 있습니다. 예상대로 Arkku의 set 기본 작업은 가장 빠릅니다.

  • Arkku의 Set Difference -First (루프 당 0.124 usec)

    mquadri$ python -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1 - l2"
    10000000 loops, best of 3: 0.124 usec per loop
  • set조회를 통한 Daniel Pryden의 목록 이해 -초 (루프 당 0.302 usec)

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "[x for x in l1 if x not in l2]"
    1000000 loops, best of 3: 0.302 usec per loop
  • 일반 목록에 대한 도넛의 목록 이해 -세 번째 (루프 당 0.552 usec)

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "[x for x in l1 if x not in l2]"
    1000000 loops, best of 3: 0.552 usec per loop
  • Moinuddin Quadri를 사용하여filter -네 번째 (루프 당 0.972 usec)

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "filter(lambda x: x not in l2, l1)"
    1000000 loops, best of 3: 0.972 usec per loop
  • 의하기 Akshay Hazari의 사용 조합 reduce+filter - 다섯 번째 (루프 당 3.97 마이크로 초)

    mquadri$ python -m timeit "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2)"
    100000 loops, best of 3: 3.97 usec per loop

추신 : set 순서를 유지하지 않고 목록에서 중복 요소를 제거합니다. 따라서 필요한 경우 설정된 차이를 사용하지 마십시오 .


32

여기에서 Donut의 답변과 다른 답변을 확장하면 목록 이해 대신 생성기 이해를 사용하고 set데이터 구조 를 사용하여 더 나은 결과를 얻을 수 있습니다 ( in연산자는 목록에서 O (n)이지만 O (1) 이므로 세트에).

다음은 귀하에게 적합한 기능입니다.

def filter_list(full_list, excludes):
    s = set(excludes)
    return (x for x in full_list if x not in s)

결과는 필터링 된 목록을 느리게 가져 오는 반복 가능합니다. 실제 목록 객체가 필요한 경우 (예 : len()결과에 대해 수행해야하는 경우 ) 다음과 같이 목록을 쉽게 작성할 수 있습니다.

filtered_list = list(filter_list(full_list, excludes))

29

Python 세트 유형을 사용하십시오. 그것은 가장 파이썬적인 것입니다. :)

또한 네이티브이기 때문에 가장 최적화 된 방법이어야합니다.

보다:

http://docs.python.org/library/stdtypes.html#set

http://docs.python.org/library/sets.htm (이전 파이썬의 경우)

# Using Python 2.7 set literal format.
# Otherwise, use: l1 = set([1,2,6,8])
#
l1 = {1,2,6,8}
l2 = {2,3,5,8}
l3 = l1 - l2

5
세트를 사용할 때 {1,3,2}는 {1,2,3}이되고 { "A", "C", "B"}는 { "A"가됩니다. "B", "C"}를 원치 않을 수도 있습니다.
Pablo Reyes

2
목록에 l1반복 요소가 포함 된 경우이 방법이 작동하지 않습니다 .
jdhao

10

사용 설정 함축 {x의 X의 L2} 또는 세트 (L2)의 세트를 얻기 위해, 다음 사용 목록 함축을 목록을 얻으려면

l2set = set(l2)
l3 = [x for x in l1 if x not in l2set]

벤치 마크 테스트 코드 :

import time

l1 = list(range(1000*10 * 3))
l2 = list(range(1000*10 * 2))

l2set = {x for x in l2}

tic = time.time()
l3 = [x for x in l1 if x not in l2set]
toc = time.time()
diffset = toc-tic
print(diffset)

tic = time.time()
l3 = [x for x in l1 if x not in l2]
toc = time.time()
difflist = toc-tic
print(difflist)

print("speedup %fx"%(difflist/diffset))

벤치 마크 테스트 결과 :

0.0015058517456054688
3.968189239501953
speedup 2635.179227x    

1
l2set = set( l2 )대신l2set = { x for x in l2 }
cz

1
좋은 영혼! 그러나 해시 가능한 객체에서만 작동한다는 점을 명심해야합니다.
Eerik Sven Puudist

7

대체 솔루션 :

reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])

2
이 방법을 사용하면 어떤 이점이 있습니까? 많은 이점없이 읽기가 더 복잡하고 읽기 어려워 보입니다.
skrrgwasme 1

복잡해 보일 수 있습니다. Reduce는 매우 유연하며 많은 목적으로 사용될 수 있습니다. fold라고합니다. reduce는 실제로 foldl입니다. 더 복잡한 것을 추가하고 싶다면이 기능에서 가능하지만 선택한 최상의 답변 인 목록 이해는 동일한 유형, 즉 폴드와 함께 동일한 유형의 목록과 아마도 같은 길이의 출력 만 가져옵니다. 출력 유형도 변경하십시오. en.wikipedia.org/wiki/Fold_%28higher-order_function%29 . 이 솔루션은 복잡도가 n * m 이하입니다. 다른 사람들은 더 나을 수도 있고 나쁠 수도 있습니다.
Akshay Hazari

1
(임의의 유형이 될 수 기능 목록, 초기 어큐뮬레이터 ()) 감소
하기 Akshay Hazari
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.