Python의 목록(배열)에서 중복 요소 제거 및 추출

사업

이 섹션에서는 목록(배열)에서 중복 요소를 제거하거나 추출하여 Python에서 새 목록을 생성하는 방법을 설명합니다.

여기에는 다음 세부 정보가 설명되어 있습니다.

  • 중복 요소 제거 및 새 목록 생성
    • 원래 목록의 순서를 유지하지 마십시오.:set()
    • 원래 목록의 순서를 유지합니다.:dict.fromkeys(),sorted()
    • 2차원 배열(목록 목록)
  • 중복 요소 추출 및 새 목록 생성
    • 원래 목록의 순서를 유지하지 마십시오.
    • 원래 목록의 순서를 유지합니다.
    • 2차원 배열(목록 목록)

목록 대신 튜플에도 동일한 개념을 적용할 수 있습니다.

에 대해서는 다음 기사를 참조하십시오.

  • 목록 또는 튜플에 중복 요소가 있는지 확인하려는 경우
  • 하나의 리스팅이 아닌 여러 리스팅에서 공통적이거나 공통적이지 않은 요소를 추출하고 싶은 경우

목록은 다양한 유형의 데이터를 저장할 수 있으며 배열과 완전히 다릅니다. 메모리 크기와 메모리 주소가 필요한 프로세스에서 배열을 처리하거나 대용량 데이터의 수치 처리를 수행하려면 배열(표준 라이브러리) 또는 NumPy를 사용하십시오.

중복 요소 제거 및 새 목록 생성

원래 목록의 순서를 유지하지 마십시오.:set()

원래 목록의 순서를 유지할 필요가 없으면 집합 유형 집합을 생성하는 set()을 사용합니다.

집합 유형은 중복 요소가 없는 데이터 유형입니다. 목록 또는 기타 데이터 유형이 set()에 전달되면 중복 값은 무시되고 고유한 값만 요소인 유형 세트의 개체가 반환됩니다.

tuple로 만들려면 tuple()을 사용하십시오.

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

print(set(l))
# {1, 2, 3, 4, 5}

print(list(set(l)))
# [1, 2, 3, 4, 5]

물론 그대로 놔둘 수도 있다. 집합 유형 집합에 대한 자세한 내용은 다음 문서를 참조하세요.

원래 목록의 순서를 유지합니다.:dict.fromkeys(),sorted()

원래 목록의 순서를 유지하려면 사전 유형의 클래스 메소드 fromkeys() 또는 내장 함수 sorted()를 사용하십시오.

dict.fromkeys()는 키가 인수에 지정된 목록, 튜플 등인 새 사전 객체를 생성합니다. 두 번째 인수가 생략되면 값은 None입니다.

사전 키에는 중복 요소가 없기 때문에 중복 값은 set()에서와 같이 무시됩니다. 또한 요소가 사전 키인 목록을 얻기 위해 사전 객체를 list()에 인수로 전달할 수 있습니다.

print(dict.fromkeys(l))
# {3: None, 2: None, 1: None, 5: None, 4: None}

print(list(dict.fromkeys(l)))
# [3, 2, 1, 5, 4]

Python 3.7(CPython은 3.6)부터 dict.fromkeys()가 인수 시퀀스의 순서를 유지한다는 것이 보장되었습니다. 이전 버전에서는 내장 함수 sorted()를 다음과 같이 사용합니다.

정렬된 요소 목록을 반환하는 sorted 인수 키에 대해 목록 튜플 메서드 index()를 지정합니다.

index()는 값의 인덱스(목록에 있는 요소의 번호)를 반환하는 메서드로, sorted()의 키로 지정하여 원본 목록의 순서에 따라 목록을 정렬할 수 있습니다. 인수 key는 호출 가능(호출 가능) 객체로 지정되므로 ()를 쓰지 마십시오.

print(sorted(set(l), key=l.index))
# [3, 2, 1, 5, 4]

2차원 배열(목록 목록)

2차원 배열(목록 목록)의 경우 set() 또는 dict.fromkeys()를 사용하는 메서드는 TypeError를 발생시킵니다.

l_2d = [[1, 1], [0, 1], [0, 1], [0, 0], [1, 0], [1, 1], [1, 1]]

# l_2d_unique = list(set(l_2d))
# TypeError: unhashable type: 'list'

# l_2d_unique_order = dict.fromkeys(l_2d)
# TypeError: unhashable type: 'list'

목록과 같은 해시할 수 없는 개체는 집합 유형의 요소나 dict 유형의 키가 될 수 없기 때문입니다.

다음 함수를 정의하십시오. 원래 목록의 순서는 유지되며 1차원 목록 및 튜플에 대해 작동합니다.

def get_unique_list(seq):
    seen = []
    return [x for x in seq if x not in seen and not seen.append(x)]

print(get_unique_list(l_2d))
# [[1, 1], [0, 1], [0, 0], [1, 0]]

print(get_unique_list(l))
# [3, 2, 1, 5, 4]

목록 이해 표기법이 사용됩니다.

여기서는 다음을 사용합니다.

  • and 연산자의 단락 평가에서 “X 및 Y”의 X가 거짓이면 Y는 평가되지 않습니다(실행되지 않음).
  • append() 메서드는 None을 반환합니다.

원래 목록 seq의 요소가 see에 존재하지 않으면 then 및 after가 평가됩니다.
see.append(x)가 실행되고 요소가 see에 추가됩니다.
append() 메서드는 None을 반환하고 None은 False이므로 not seen.append(x)는 True로 평가됩니다.
목록 내포 표기법의 조건식은 True가 되어 최종 생성된 목록의 요소로 추가됩니다.

원래 목록 seq의 요소가 see에 있는 경우 x not in see는 False이고 목록 이해 표현식에 대한 조건식은 False입니다.
따라서 최종 생성 목록의 요소로 추가되지 않습니다.

다른 방법은 결과가 정렬되지만 NumPy의 함수 np.unique()에서 인수 축을 설정하는 것입니다.

중복 요소 추출 및 새 목록 생성

원래 목록의 순서를 유지하지 마십시오.

원본 목록에서 중복 요소만 추출하려면 collections.Counter()를 사용합니다.
요소를 키로, 요소 수를 값으로 포함하는 collections.Counter(사전의 하위 클래스)를 반환합니다.

import collections

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

print(collections.Counter(l))
# Counter({3: 3, 2: 2, 1: 2, 5: 1, 4: 1})

이것은 사전의 하위 클래스이기 때문에 items()를 사용하여 키와 값을 검색할 수 있습니다. 숫자가 2개 이상인 키만 추출하면 됩니다.

print([k for k, v in collections.Counter(l).items() if v > 1])
# [3, 2, 1]

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

위의 예에서 볼 수 있듯이 Python 3.7부터 collections.Counter의 키는 원래 목록의 순서를 유지하는 식입니다.

이전 버전에서는 중복 요소를 삭제하는 것처럼 sorted()를 사용한 정렬로 충분합니다.

print(sorted([k for k, v in collections.Counter(l).items() if v > 1], key=l.index))
# [3, 2, 1]

중복을 그대로 추출하려면 원본 목록의 요소를 둘 이상으로 그대로 두십시오. 순서도 유지됩니다.

cc = collections.Counter(l)
print([x for x in l if cc[x] > 1])
# [3, 3, 2, 1, 1, 2, 3]

2차원 배열(목록 목록)

2차원 배열(목록의 목록)의 경우 원래 목록의 순서가 유지되지 않은 경우와 유지되는 경우 각각 다음과 같은 기능이 가능합니다. 1차원 목록과 튜플에서도 작동합니다.

l_2d = [[1, 1], [0, 1], [0, 1], [0, 0], [1, 0], [1, 1], [1, 1]]
def get_duplicate_list(seq):
    seen = []
    return [x for x in seq if not seen.append(x) and seen.count(x) == 2]

def get_duplicate_list_order(seq):
    seen = []
    return [x for x in seq if seq.count(x) > 1 and not seen.append(x) and seen.count(x) == 1]

print(get_duplicate_list(l_2d))
# [[0, 1], [1, 1]]

print(get_duplicate_list_order(l_2d))
# [[1, 1], [0, 1]]

print(get_duplicate_list(l))
# [3, 1, 2]

print(get_duplicate_list_order(l))
# [3, 2, 1]

중복으로 추출하려면 원래 목록의 요소를 2개 이상으로 남겨둡니다.

print([x for x in l_2d if l_2d.count(x) > 1])
# [[1, 1], [0, 1], [0, 1], [1, 1], [1, 1]]

count()의 계산 복잡도가 O(n)이므로 위에서 설명한 count()를 반복적으로 실행하는 함수는 매우 비효율적입니다. 더 현명한 방법이 있을 수 있습니다.

Counter는 사전의 하위 클래스이므로 요소가 목록 또는 기타 해시 불가능한 객체인 목록이나 튜플을 collections.Counter()에 전달하면 오류가 발생하고 사용할 수 없게 됩니다.

# print(collections.Counter(l_2d))
# TypeError: unhashable type: 'list'