리스트에서 중복된 요소를 제거하는 방법
- 리스트에서 중복된 요소를 제거하는 방법에는 크게 두 가지가 있다.
1. 자료구조 SET을 이용하는 방법
2. 딕셔너리를 이용하는 방법
첫 번째 방법 - SET() 자료구조 이용
- 파이썬에서는 중복된 요소를 허용하지 않는 SET 이라는 자료구조가 있다.
nums = [1, 3, 5, 7, 9, 2, 4, 6, 6, 5, 4]
sets = set(nums) # 결과: {1, 2, 3, 4, 5, 7, 8, 9}
list(sets) # 결과: [1, 2, 3, 4, 5, 7, 8, 9]
- 중복이 제거된 집합을 생성하고나서, 이를 다시 리스트로 변환하면 중복이 제거된 리스트가 반환된다.
- 공식 문서를 살펴보면,
set()
은 순서를 보장하지 않는 컬렉션이며, 중복을 허용하지 않는다고 되어있다.
두 번째 방법 - 딕셔너리를 이용하는 방법
- 두 번째 방법은 딕셔너리의 키가 고유하다는 특성을 이용하여 중복을 제거하는 것이다.
nums = [1, 3, 5, 7, 9, 2, 4, 6, 6, 5, 4]
list(set(dict.fromkeys(nums))) # 결과: [1, 3, 5, 7, 9, 2, 4, 6]
-
SET
을 이용한 방법과 차이점은 중복된 요소를 제거하더라도, 순서를 유지한다는 점이다. -
딕셔너리 자료구조는 중복된 키 값이 입력되었을 때 값만 덮어쓰여지고 키 값은 유지하는 특성을 이용한 것이다.
fromkeys()
는 반복 가능한 값을 인자로 받아, 키로 사용한다 그리고 그 키 값으로 새로운 딕셔너리 타입을 만드는 것을 확인할 수 있다.
>> nums1 = [1, 3, 5, 7, 9, 2, 4, 6, 6, 5, 4]
>> dict.fromkeys(nums1)
{1: None, 3: None, 5: None, 7: None, 9: None, 2: None, 4: None, 6: None}
- 사용가능한 버전에는 키만 인자로 받는 버전과, 키와 값을 둘다 받는 버전이 있는데 인자로 키만 주어질 경우 키 값만 생성되고 키에 대한 값들은 모두
None
으로 설정된다.
-
딕셔너리에서
list(d)
를 수행하였을 때 딕셔너리에 존재하는 모든 키들이 삽입 순서대로 반환된다고 되어 있다. -
따라서 리스트의 순서를 유지하고 싶으면, 딕셔너리를 이용하여 중복 요소를 제거하는 것이 좋다.
성능 비교
-
그렇다면 순서를 보장하지 않아도 되는 리스트에서 중복을 제거할 때 어떤 것이 더 좋을까?
-
이를 알아보기 위해서 성능을 비교해보았다.
pip install pytest-benchmark
- 우선 성능 비교를 위해서
pytest-benchmark
모듈을 설치한다.
import time
import random
import pytest
nums = list(random.randint(1, 100) for _ in range(100))
@pytest.mark.benchmark(
group="group-name",
min_rounds=10000,
timer=time.time,
disable_gc=True,
warmup=True
)
def test_use_dict(benchmark):
@benchmark
def func1():
list(dict.fromkeys(nums))
@pytest.mark.benchmark(
group="group-name",
min_rounds=10000,
timer=time.time,
disable_gc=True,
warmup=True
)
def test_use_set(benchmark):
@benchmark
def func2():
list(set(nums))
-
맨 위에서 랜덤으로 1 ~ 100 사이의 정수 리스트를 만들어 준다.
-
위의 코드는
SET()
과 딕셔너리를 이용하여, 리스트에서 중복된 요소를 제거하는 함수를 벤치마크 한 결과이다. -
각각 10,000,000 번씩 돌린 결과의 수치들을 뽑아내었다.
-
랜덤으로 생성된 리스트의 특성에 따라서 차이가 심할 수 있으므로 평균, 표준 편차, 중앙값이 의미가 있을 것이다.
-
딕셔너리를 이용하는 것보다
SET
을 사용하는 것이 중복된 원소를 훨씬 빠르게 제거할 수 있다는 것을 확인할 수 있다. 다만 리스트의 순서를 유지할 필요가 없는 경우에 사용할 수 있을 것이다.