[python] 파이썬리스트 빼기 연산

나는 이것과 비슷한 것을하고 싶다 :

>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1,3,5,7,9]
>>> y
[1, 3, 5, 7, 9]
>>> y - x   # (should return [2,4,6,8,0])

그러나 이것은 파이썬 목록에서 지원하지 않습니다. 가장 좋은 방법은 무엇입니까?



답변

목록 이해력을 사용하십시오.

[item for item in x if item not in y]

-infix 구문 을 사용하려면 다음을 수행하십시오.

class MyList(list):
    def __init__(self, *args):
        super(MyList, self).__init__(args)

    def __sub__(self, other):
        return self.__class__(*[item for item in self if item not in other])

그런 다음 다음과 같이 사용할 수 있습니다.

x = MyList(1, 2, 3, 4)
y = MyList(2, 5, 2)
z = x - y   

그러나 목록 속성 (예 : 순서)이 절대적으로 필요하지 않은 경우 다른 답변에서 권장하는대로 세트를 사용하십시오.


답변

설정된 차이 사용

>>> z = list(set(x) - set(y))
>>> z
[0, 8, 2, 4, 6]

또는 x와 y 만 설정하면 변환을 수행 할 필요가 없습니다.


답변

이것은 “세트 빼기”연산입니다. 이를 위해 설정된 데이터 구조를 사용하십시오.

파이썬 2.7에서 :

x = {1,2,3,4,5,6,7,8,9,0}
y = {1,3,5,7,9}
print x - y

산출:

>>> print x - y
set([0, 8, 2, 4, 6])


답변

중복 및 주문 품목에 문제가있는 경우 :

[i for i in a if not i in b or b.remove(i)]

a = [1,2,3,3,3,3,4]
b = [1,3]
result: [2, 3, 3, 3, 4]


답변

많은 사용 사례에서 원하는 답변은 다음과 같습니다.

ys = set(y)
[item for item in x if item not in ys]

이것은 aaronasterling의 답변quantumSoup의 답변 사이의 하이브리드 입니다.

aaronasterling의 버전은의 len(y)각 요소에 대한 항목 비교를 수행 x하므로 2 차 시간이 걸립니다. quantumSoup의 버전을 사용하는 세트는, 그래서 각 요소에 대해 하나의 일정 시간의 집합을 조회하지 x가 변환하기 때문에, -하지만를 모두 xy세트로, 그것은 당신의 요소의 순서를 잃는다.

y세트 로만 변환 x하고 순서를 반복 하면 선형 시간과 순서 보존이라는 두 가지 이점을 모두 누릴 수 있습니다. *


그러나 이것은 여전히 ​​quantumSoup의 버전에서 문제가 있습니다 : 요소를 해시 할 수 있어야합니다. 예를 들어, 다른 dicts 목록에서 dicts 목록을 빼려고하지만 뺄 목록이 큰 경우 어떻게해야합니까?

해시가 가능한 방식으로 값을 꾸밀 수 있다면 문제가 해결됩니다. 예를 들어, 값 자체가 해시 가능한 플랫 사전의 경우 :

ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]

유형이 조금 더 복잡한 경우 (예 : 종종 해시 가능한 JSON 호환 값 또는 값이 재귀 적으로 동일한 유형 인 목록 또는 딕트를 처리하는 경우)이 솔루션을 계속 사용할 수 있습니다. 그러나 일부 유형은 해시 가능한 것으로 변환 할 수 없습니다.


아이템이 해시 가능하지 않고 해시 가능하지만 비교 가능하다면 적어도 로그 선형 시간을 얻을 수 있습니다 ( O(N*log M), 이는 O(N*M)목록 솔루션 시간 보다 훨씬 좋지만 좋지는 않습니다. O(N+M)정렬하고 사용하여 설정된 솔루션 의 시간) bisect:

ys = sorted(y)
def bisect_contains(seq, item):
    index = bisect.bisect(seq, item)
    return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]

아이템이 해시 가능하거나 비교 가능하지 않으면 2 차 솔루션에 갇혀 있습니다.


* OrderedSet레시피 및 타사 모듈을 찾을 수 있는 한 쌍의 객체 를 사용하여이 작업을 수행 할 수도 있습니다. 그러나 나는 이것이 더 간단하다고 생각합니다.

** 집합 조회가 일정한 시간 인 이유는 값을 해시하고 해당 해시에 대한 항목이 있는지 확인하기 때문입니다. 값을 해시 할 수 없으면 작동하지 않습니다.


답변

세트에서 값을 찾는 것이 목록에서 찾는 것보다 빠릅니다.

[item for item in x if item not in set(y)]

나는 이것이 다음보다 약간 더 잘 확장 될 것이라고 믿는다.

[item for item in x if item not in y]

둘 다 목록의 순서를 유지합니다.


답변

목록에 중복 요소가 허용되면 컬렉션의 카운터를 사용할 수 있습니다.

from collections import Counter
result = list((Counter(x)-Counter(y)).elements())

x의 요소 순서를 유지해야하는 경우 :

result = [ v for c in [Counter(y)] for v in x if not c[v] or c.subtract([v]) ]