어제 오늘 내일

[Python] "리스트 안의 리스트?" 2차원 리스트 중복 제거 본문

IT/Python

[Python] "리스트 안의 리스트?" 2차원 리스트 중복 제거

hi.anna 2026. 4. 17. 08:44

1차원 리스트의 중복을 제거할 때는 마법의 주머니 set()을 쓰면 아주 쉽게 해결되었습니다.
하지만 실무에서 엑셀 데이터나 표 형태의 데이터를 다루다 보면 리스트 안에 또 리스트가 들어있는 '2차원 리스트'를 만나게 됩니다.

여기에 평소 하던 대로 set()을 씌우면 파이썬은 TypeError: unhashable type: 'list'라는 무시무시한 에러를 뿜어냅니다. 오늘은 이 에러가 왜 발생하는지 알아보고, 2차원 리스트의 중복을 3가지 방법으로 완벽하게 날려버리는 비법을 전수해 드립니다.


1. 왜 그냥 set()을 쓰면 에러가 날까요?

파이썬의 집합(set)이나 딕셔너리의 이름표(Key)에는 아주 엄격한 규칙이 하나 있습니다. 바로 "내용물이 중간에 바뀔 위험이 있는 데이터(Mutable)는 절대 들어올 수 없다"는 것입니다.

우리가 아는 리스트([]).append()로 값을 추가하거나 수정할 수 있는 자유로운 영혼입니다. 반면, 이전에 배웠던 튜플(())은 한 번 만들어지면 절대 수정할 수 없는 자물쇠 채운 상자(Immutable)죠.

파이썬은 내용물이 변할 수 있는 리스트를 집합에 넣는 것을 불안해해서 거부합니다. 따라서 해결책은 아주 간단합니다. 잠깐 자물쇠(튜플)를 채워서 중복을 제거하고, 다시 리스트로 풀어주면 됩니다.


2. 방법 1: 튜플(Tuple)로 변신시켜서 중복 날리기 (순서 무시)

가장 기본적이고 파이썬다운 방법입니다. 지난 시간에 배운 '리스트 컴프리헨션'을 활용하면 코드가 아주 깔끔해집니다.

📌 예제 코드: settuple의 콜라보

# 이름, 나이, 부서가 담긴 2차원 리스트 (홍길동 데이터가 중복됨)
data = [
    ["홍길동", 30, "영업팀"],
    ["김철수", 25, "개발팀"],
    ["홍길동", 30, "영업팀"],  # 완벽히 똑같은 중복 데이터!
    ["이영희", 28, "디자인팀"]
]

# 1. 안쪽 리스트들을 모두 튜플()로 바꾼 뒤, set()에 넣어 중복을 날립니다.
# (이때 데이터의 원래 순서는 뒤죽박죽 섞이게 됩니다.)
unique_tuples = set(tuple(row) for row in data)

# 2. 튜플로 묶여있던 데이터를 다시 리스트[]로 풀어줍니다.
clean_data = [list(row) for row in unique_tuples]

print(clean_data)
# 출력 예시 (순서는 다를 수 있음): 
# [['이영희', 28, '디자인팀'], ['홍길동', 30, '영업팀'], ['김철수', 25, '개발팀']]

3. 방법 2: 원래 순서까지 완벽하게 지키기 (dict.fromkeys)

크롤링을 했거나 시간순으로 쌓인 데이터라서 "처음 들어온 순서를 무조건 유지"해야 한다면, 1차원 리스트 때 썼던 딕셔너리 꼼수를 똑같이 적용하면 됩니다.

📌 예제 코드: 순서 유지하며 중복 제거하기

data = [
    ["홍길동", 30, "영업팀"],
    ["김철수", 25, "개발팀"],
    ["홍길동", 30, "영업팀"],  # 중복
    ["이영희", 28, "디자인팀"]
]

# 1. 안쪽 리스트를 튜플로 바꿔서 딕셔너리의 Key로 만듭니다. (중복 제거 + 순서 기억)
dict_data = dict.fromkeys(tuple(row) for row in data)

# 2. 딕셔너리의 Key들만 쏙쏙 뽑아서 다시 리스트로 감싸줍니다.
ordered_clean_data = [list(key) for key in dict_data]

print("순서 유지 결과:")
for row in ordered_clean_data:
    print(row)

실행 결과:

순서 유지 결과:
['홍길동', 30, '영업팀']
['김철수', 25, '개발팀']
['이영희', 28, '디자인팀']

홍길동이 맨 처음에 등장했던 그 순서 그대로 예쁘게 살아남았습니다!


4. 방법 3: 보너스 트랙! 판다스(Pandas)로 1초 컷

만약 여러분이 엑셀처럼 방대한 데이터를 다루고 있고 이미 pandas 패키지를 알고 계신다면, 저렇게 복잡하게 튜플로 변환할 필요가 전혀 없습니다. 판다스는 2차원 리스트(표 형태)를 다루는 끝판왕이기 때문입니다.

📌 예제 코드: 판다스의 drop_duplicates()

import pandas as pd

data = [
    ["홍길동", 30, "영업팀"],
    ["김철수", 25, "개발팀"],
    ["홍길동", 30, "영업팀"],
    ["이영희", 28, "디자인팀"]
]

# 1. 2차원 리스트를 판다스 데이터프레임으로 만듭니다.
df = pd.DataFrame(data)

# 2. 중복을 날리고(.drop_duplicates()), 다시 리스트로 되돌립니다(.values.tolist())
pandas_clean_data = df.drop_duplicates().values.tolist()

print(pandas_clean_data)
# 출력: [['홍길동', 30, '영업팀'], ['김철수', 25, '개발팀'], ['이영희', 28, '디자인팀']]

단 한 줄(df.drop_duplicates().values.tolist())로 순서까지 갓벽하게 유지하면서 중복을 없앴습니다. 실무에서 데이터 분석가들이 가장 사랑하는 방식입니다.


📝 마치며

핵심 요약입니다.
2차원 리스트는 그냥 set()에 넣으면 에러가 납니다! 따라서 안쪽 데이터들을 자물쇠 채운 튜플(tuple)로 변환하는 과정을 거쳐야 합니다.

  • 순서 상관없을 때: [list(x) for x in set(tuple(x) for x in data)]
  • 순서 지켜야 할 때: [list(x) for x in dict.fromkeys(tuple(x) for x in data)]
  • 판다스를 쓸 줄 안다면: pd.DataFrame(data).drop_duplicates().values.tolist()

이제 아무리 복잡하게 얽힌 다차원 리스트를 만나도 가뿐하게 중복을 날려버리실 수 있습니다.

 

반응형
Comments