메뉴 닫기

파이썬 pandas 중복 제거 완벽 가이드 duplicated subset keep drop_duplicates

파이썬 pandas 중복 제거 완벽 가이드 duplicated subset keep drop_duplicates

🐼 데이터프레임 중복을 한 번에 정리하는 실전 코드와 개념을 깔끔하게 정리합니다

데이터 분석을 하다 보면 같은 값이 반복되어 통계가 부풀려지거나 그룹화 결과가 왜곡되는 경험을 자주 하게 됩니다.
특히 대용량 CSV를 합치거나 외부 시스템에서 추출한 로그를 다룰 때는 눈에 보이지 않는 미세한 중복이 결과 품질을 좌우하곤 하죠.
그래서 이번 글은 pandas에서 중복을 식별하는 duplicated와 중복 행을 실제로 제거하는 drop_duplicates를 중심으로 정리합니다.
필드 일부만 비교하고 싶을 때 쓰는 subset, 유지할 레코드 위치를 정하는 keep의 의미까지 하나씩 이해하면, 같은 데이터라도 훨씬 단단한 분석 파이프라인을 만들 수 있습니다.
헷갈리기 쉬운 옵션 차이, 성능 팁, 실무에서 바로 쓰는 패턴까지 자연스럽게 이어지는 흐름으로 담았습니다.

핵심은 간단합니다.
무엇을 중복으로 볼지를 명확히 정의하고, 그 정의를 코드로 옮기는 것입니다.
이 글은 pandas 사용자라면 반드시 알아야 할 duplicated의 불리언 마스크 활용법, drop_duplicates의 안전한 적용 순서, 그리고 subsetkeep 조합으로 원하는 결과를 얻는 실전 규칙을 예제로 설명합니다.
중복이 분석 지표를 어떻게 왜곡하는지, 그리고 작은 옵션 차이가 결과를 어떻게 바꾸는지도 함께 짚어보겠습니다.
데이터가 커질수록 사소한 결정이 큰 차이를 만듭니다.
탄탄한 기준과 검증 가능한 코드로 중복 문제를 말끔히 정리해 봅시다.



🔗 pandas duplicated 사용법과 원리

duplicated는 DataFrame 또는 Series에서 중복으로 간주되는 행의 불리언 마스크를 반환하는 메서드입니다.
즉, 어떤 기준으로 이미 등장한 값과 동일한 레코드인지 판별해 True 또는 False를 표시합니다.
이 마스크를 활용하면 중복만 선택하거나 제거 대상으로 표시하는 등 후속 처리의 기준점을 안정적으로 잡을 수 있습니다.
핵심 옵션은 subsetkeep이며, 반환값 자체는 원본을 바꾸지 않는다는 점이 중요합니다.
실무에서는 이 마스크를 집계 전 필터링, 로그 클린징, 키 충돌 검사, 최신/최초 이벤트 선별 등에 널리 사용합니다.

🧠 동작 원리와 반환값

기본적으로 duplicated()는 위에서 아래로 순회하며 이전에 본 값과 동일한지 비교합니다.
기준 컬럼을 지정하지 않으면 전체 컬럼 조합을 한 행의 키로 간주합니다.
처음 등장하는 레코드는 False, 이후 같은 조합이 다시 나타나면 True로 표시합니다.
이 결과는 Series(Boolean) 형태이며, 길이는 원본 DataFrame과 동일합니다.
이 마스크를 인덱싱에 사용하면 중복만, 또는 비중복만 손쉽게 분리할 수 있습니다.

CODE BLOCK
import pandas as pd

df = pd.DataFrame({
    "user_id": [1, 1, 2, 2, 2, 3],
    "event":   ["login", "login", "pay", "pay", "logout", "login"],
    "ts":      [1, 2, 3, 4, 5, 6]
})

# 전체 열 조합 기준 중복 마스크
mask_all = df.duplicated()
# 특정 열만 기준(subset)으로 중복 마스크
mask_uid  = df.duplicated(subset=["user_id"])

print(mask_all.tolist())  # [False, True, False, True, False, False]
print(mask_uid.tolist())  # [False, True, False, True, True, False]

⚙️ keep 파라미터의 의미

keep은 어떤 항목을 중복으로 간주하지 않고 남길지를 정합니다.
같은 키가 여러 번 나타날 때, 최초 또는 마지막만 남기고 나머지를 중복으로 표시하도록 제어합니다.
값은 ‘first’ (기본값), ‘last’, False 중에서 선택합니다.

keep 값 동작 설명
‘first’ 처음 등장한 레코드를 고유로 보고, 이후 동일 키는 True(중복)로 표시
‘last’ 마지막 등장한 레코드를 고유로 보고, 이전 동일 키는 True(중복)로 표시
False 해당 키가 여러 번 나오면 모두 True(중복)로 표시
CODE BLOCK
# keep 옵션 비교
df[ df.duplicated(subset=["user_id"], keep="first") ]   # 최초를 남기고 나머지 중복 표시
df[ df.duplicated(subset=["user_id"], keep="last") ]    # 마지막을 남기고 이전을 중복 표시
df[ df.duplicated(subset=["user_id"], keep=False) ]     # 동일 user_id 전부를 중복으로 표시

🛠️ subset으로 비교 기준 정하기

subset은 중복 판단에 사용할 컬럼 집합을 지정합니다.
예를 들어 고객 식별자와 구매일만 동일하면 중복 주문으로 간주하고 싶을 때 subset=[“customer_id”,”order_date”]처럼 설정합니다.
문자열 공백, 대소문자 차이, 타입 불일치 등은 중복 판정에 영향을 줄 수 있으므로 사전 정규화(트림, 소문자화, 형 변환)를 함께 적용하는 습관이 좋습니다.

CODE BLOCK
# 주문 기준 중복 감지
cols = ["customer_id", "order_date"]
dups = df.duplicated(subset=cols, keep="first")
duplicates_only = df[dups]

# 비중복만 남기기
unique_only = df[~dups]

💡 TIP: duplicated는 데이터 변경 없이 마스크만 반환합니다.
실제 삭제는 drop_duplicates로 수행하세요.
먼저 마스크로 결과를 눈으로 확인하고, 조건이 정확할 때 제거를 적용하면 안전합니다.

⚠️ 주의: 타임스탬프가 초 단위까지만 저장되거나 문자열 트림이 안 된 경우, 의도치 않게 서로 다른 값으로 인식될 수 있습니다.
중복 기준이 되는 열은 전처리(트림, 케이스 정규화, 형 변환)를 확실히 한 다음 subset에 지정하세요.

  • 🧹공백·대소문자·타입 정규화 후 중복 판단
  • 🧩업무 규칙에 맞는 subset 정의
  • 🧭보존 전략을 keep=’first’|’last’|False로 명확히 지정
  • 🔍삭제 전 duplicated 마스크를 시각 확인

🛠️ subset 옵션으로 컬럼 지정하기

subset은 pandas의 duplicateddrop_duplicates에서 모두 사용되는 핵심 인자입니다.
이 옵션은 “중복을 판단할 때 어떤 열(컬럼)을 기준으로 비교할 것인지”를 지정하는 역할을 합니다.
즉, 모든 열이 아니라 일부 주요 열만 비교해 중복을 검출하거나 제거하고 싶을 때 꼭 필요합니다.
특히 고객 데이터, 주문 데이터, 로그 데이터 등에서는 여러 열 중 특정 키 조합만이 중복 판단의 기준이 되는 경우가 많습니다.

기본적으로 subset을 지정하지 않으면 전체 컬럼 조합이 중복 판정 기준이 됩니다.
하지만 예를 들어 ‘고객 아이디’와 ‘날짜’만 같으면 중복으로 처리하고 싶을 때는 아래처럼 명시해야 합니다.
컬럼 이름 하나만 지정할 때는 문자열로, 여러 개를 지정할 때는 리스트 형태로 전달하면 됩니다.

CODE BLOCK
import pandas as pd

df = pd.DataFrame({
    "customer_id": [101, 101, 102, 103, 103],
    "order_date": ["2024-07-01", "2024-07-01", "2024-07-03", "2024-07-05", "2024-07-05"],
    "amount": [20000, 20000, 15000, 18000, 18000]
})

# 특정 컬럼 조합으로 중복 판정
df.duplicated(subset=["customer_id", "order_date"], keep="first")

이 결과는 같은 고객이 같은 날짜에 주문한 데이터가 두 번째 등장했을 때 True로 표시됩니다.
이처럼 subset은 불필요한 중복을 효율적으로 탐지하는 필터 역할을 하며, 실제 데이터 분석이나 리포트 생성 전 데이터 클렌징 단계에서 가장 많이 활용됩니다.

💡 subset 적용 시 주의할 점

중복을 정확히 판별하려면 비교 대상 열의 값이 완전히 동일해야 합니다.
즉, 데이터 타입이 다르거나 공백, 대소문자 차이가 존재하면 중복으로 인식되지 않을 수 있습니다.
예를 들어 ‘A001’과 ‘a001’은 문자열로 보면 서로 다르기 때문에, 동일한 고객으로 취급해야 한다면 사전 처리 과정에서 일관된 형식으로 변환해야 합니다.

⚠️ 주의: subset으로 지정한 열에 결측치(NaN)가 포함된 경우, pandas는 이를 고유 값으로 인식합니다.
따라서 NaN을 가진 행들은 서로 다른 값으로 간주되어 중복으로 처리되지 않습니다.

🔍 subset의 실제 활용 예시

실무에서는 여러 로그 소스를 병합하거나, ERP·CRM·마케팅 데이터 등 서로 다른 시스템의 레코드를 합칠 때 subset으로 중복 기준을 정의합니다.
예를 들어 회원가입 로그와 로그인 로그를 비교할 때, 이메일 주소와 가입 일자를 기준으로 중복을 제거하면 중복 회원 탐지에 유용합니다.

CODE BLOCK
# 이메일과 날짜 기준 중복 제거
df_clean = df.drop_duplicates(subset=["email", "signup_date"], keep="first")

# 또는 중복 데이터만 추출
dup_emails = df[df.duplicated(subset=["email", "signup_date"], keep=False)]

💬 subset은 데이터를 그룹화하는 핵심 키를 명시하는 개념으로 이해하면 쉽습니다.
그룹 기준을 어떻게 잡느냐에 따라 ‘중복’의 정의가 완전히 달라집니다.

  • 🔑비교 기준이 되는 컬럼을 명확히 지정
  • 🧹공백, 대소문자, NaN 등 형식 불일치 정리
  • ⚙️duplicateddrop_duplicates에 동일하게 사용 가능
  • 🔍중복 기준이 달라지면 결과도 달라진다는 점을 명심



⚙️ keep 파라미터로 중복 기준 선택

pandas의 keep 파라미터는 duplicateddrop_duplicates가 중복을 처리할 때 어떤 행을 남길지를 결정합니다.
같은 키 조합이 여러 번 등장했을 때 “첫 번째”, “마지막”, “모두 제거” 중 어떤 방식을 쓸지 이 옵션으로 지정합니다.
기본값은 ‘first’로, 첫 번째 항목을 남기고 이후의 동일한 항목들을 중복으로 처리합니다.
데이터의 시간순 정렬 여부나 최신 레코드 보존 규칙에 따라 ‘last’ 또는 False로 변경할 수 있습니다.

🧭 keep의 세 가지 모드 비교

옵션 설명 활용 예시
keep=’first’ 첫 번째 중복값만 남기고 이후의 동일한 값들은 제거합니다. 로그에서 최초 방문 기록만 유지할 때
keep=’last’ 마지막 중복값만 남기고 이전의 동일한 값들을 제거합니다. 가장 최근 업데이트된 데이터만 남기고 싶을 때
keep=False 중복된 모든 행을 제거합니다. 즉, 유일한 값만 남습니다. 정확히 한 번만 등장한 값만 남기고 싶을 때
CODE BLOCK
df = pd.DataFrame({
    "name": ["Kim", "Lee", "Lee", "Park", "Kim"],
    "score": [90, 85, 85, 80, 90]
})

print(df.duplicated(subset=["name"], keep="first"))
# [False, False, True, False, True]

print(df.duplicated(subset=["name"], keep="last"))
# [True, False, False, False, False]

print(df.duplicated(subset=["name"], keep=False))
# [True, False, True, False, True]

위 예시를 보면 같은 이름이 여러 번 등장할 때, keep 설정에 따라 True/False의 패턴이 완전히 달라집니다.
이 차이를 이해해야 drop_duplicates()를 안전하게 사용할 수 있습니다.

📊 keep을 잘못 지정했을 때의 문제

데이터가 시간순으로 정렬되지 않았거나, 고유키를 잘못 지정했을 경우 keep=’first’를 사용하면 실제로 남겨야 할 최신 데이터가 삭제될 수 있습니다.
이런 경우에는 정렬 기준을 먼저 명확히 설정한 뒤 중복 제거를 실행해야 합니다.

CODE BLOCK
# 최신 데이터 기준으로 정렬 후 중복 제거
df_sorted = df.sort_values(by="update_time", ascending=True)
clean = df_sorted.drop_duplicates(subset=["user_id"], keep="last")

💡 TIP: keep 옵션은 ‘중복을 제거할 때 남기는 기준’입니다.
즉, 데이터 순서가 잘못되어 있으면 keep 설정이 올바르게 작동하지 않습니다.
필요할 경우 sort_values()로 정렬을 먼저 수행하세요.

💬 keep은 데이터의 “보존 기준”을 결정하는 장치입니다.
‘어떤 것을 남길 것인가’는 데이터 품질에 직접 영향을 주는 중요한 선택입니다.

  • 🧩keep=’first’는 기본값이며, 첫 번째 항목만 남긴다
  • 🕒시간 기준 최신 데이터 보존 시 keep=’last’
  • 🚫모두 제거할 때는 keep=False로 설정
  • ⚠️데이터가 정렬되지 않은 상태에서 keep을 쓰면 예상치 못한 결과 발생

🧹 drop_duplicates로 중복 행 제거

pandas에서 실제로 중복 데이터를 제거할 때 사용하는 메서드는 drop_duplicates()입니다.
이 함수는 duplicated()의 원리를 그대로 따라가면서, 중복 행을 DataFrame에서 직접 삭제한 새로운 결과를 반환합니다.
기본적으로 원본 데이터는 그대로 유지되며, inplace=True 옵션을 주면 원본 자체를 수정할 수도 있습니다.

CODE BLOCK
import pandas as pd

df = pd.DataFrame({
    "name": ["Kim", "Lee", "Lee", "Park", "Kim"],
    "age": [25, 32, 32, 29, 25],
    "city": ["Seoul", "Busan", "Busan", "Incheon", "Seoul"]
})

# 중복 제거
unique_df = df.drop_duplicates()

print(unique_df)

위 예제는 완전히 동일한 행을 제거합니다.
즉, 모든 열의 값이 동일한 경우만 중복으로 간주됩니다.
만약 특정 열만 비교 기준으로 사용하고 싶다면 subset 옵션을 사용해야 합니다.

📑 subset + keep 조합으로 원하는 결과 만들기

subset과 keep을 함께 사용하면, 비교 기준과 남길 행의 위치를 세밀하게 제어할 수 있습니다.
예를 들어 고객 정보 중 이메일(email)만 기준으로 중복을 제거하고, 가장 마지막에 입력된 정보를 남기고 싶다면 다음과 같이 작성합니다.

CODE BLOCK
# 이메일 기준으로 중복 제거 (마지막 값 유지)
clean_df = df.drop_duplicates(subset=["email"], keep="last")

이 방법은 CRM, 로그 데이터, 회원가입 기록 등에서 가장 최근 상태를 보존하고 싶을 때 자주 사용됩니다.
특히 drop_duplicates()는 내부적으로 정렬 순서에 의존하기 때문에, 반드시 정렬 기준을 사전에 명시하는 것이 좋습니다.

🧾 inplace 옵션으로 원본 수정하기

기본적으로 drop_duplicates()는 새로운 DataFrame을 반환합니다.
하지만 inplace=True를 사용하면 반환값 없이 원본 자체를 수정합니다.
이 옵션은 메모리를 절약하고 간단한 데이터 정리 작업에 유용하지만, 되돌리기 어렵기 때문에 신중히 사용해야 합니다.

CODE BLOCK
# 원본에서 직접 제거
df.drop_duplicates(subset=["user_id"], keep="first", inplace=True)

⚠️ 주의: inplace=True는 되돌리기가 불가능하므로, 테스트 단계에서는 항상 False로 두고 결과를 확인한 뒤 사용하는 것이 안전합니다.

💎 실무 예시: 중복 사용자 로그 제거

대규모 로그 파일에서 같은 사용자가 같은 행동을 여러 번 수행한 경우, 중복을 제거하면 분석 정확도를 높일 수 있습니다.

CODE BLOCK
df = pd.DataFrame({
    "user_id": [1, 1, 2, 2, 3, 3],
    "action": ["login", "login", "click", "click", "logout", "logout"],
    "time": ["09:01", "09:01", "09:05", "09:05", "09:10", "09:10"]
})

clean_logs = df.drop_duplicates(subset=["user_id", "action", "time"])
print(clean_logs)

💎 핵심 포인트:
중복 제거는 데이터의 신뢰도를 높이는 필수 단계입니다.
중복 기준(subset)과 남길 값(keep)을 명확히 정의해야만 원하는 데이터만 깔끔하게 남길 수 있습니다.

  • 🧩중복 기준은 subset으로 명시
  • 🧭남길 행은 keep=’first’|’last’|False로 지정
  • ⚙️원본 수정은 inplace=True로 수행
  • 💾중복 제거 전에는 항상 백업 또는 복사본 작업 권장



🚀 성능 팁과 실무 패턴

pandas의 duplicated()drop_duplicates()는 편리하지만, 대용량 데이터셋에서는 성능 이슈가 생길 수 있습니다.
데이터의 크기가 수백만 행을 넘을 경우, 단일 CPU 환경에서는 중복 검사와 해시 비교 과정이 병목이 되기 때문입니다.
아래는 실제 분석 환경에서 중복 제거를 효율적으로 수행하기 위한 핵심 팁과 실무 패턴입니다.

⚡ 1. 중복 제거 전 데이터 타입 최적화

중복 판정은 내부적으로 해시(hash) 연산을 이용하므로, 열의 데이터 타입이 효율적일수록 처리 속도가 빨라집니다.
예를 들어 숫자열은 int32, 카테고리형 문자열은 category 타입으로 변환하면 메모리 사용량과 연산 속도를 크게 줄일 수 있습니다.

CODE BLOCK
df["city"] = df["city"].astype("category")
df["user_id"] = df["user_id"].astype("int32")

df.drop_duplicates(subset=["user_id", "city"], keep="first", inplace=True)

🧩 2. 여러 단계의 중복 정제 패턴

실무에서는 단일 기준으로만 중복을 제거하는 것이 아니라, 여러 조건을 순차적으로 적용해야 할 때가 많습니다.
예를 들어, ‘이메일 중복 → 날짜 중복 → 아이디 중복’ 순으로 제거하는 방식이 있습니다.

CODE BLOCK
df = df.drop_duplicates(subset=["email"], keep="last")
df = df.drop_duplicates(subset=["date"], keep="last")
df = df.drop_duplicates(subset=["user_id"], keep="first")

이런 다단계 정제는 데이터의 신뢰성을 높이지만, 실행 순서에 따라 결과가 달라질 수 있으므로 반드시 순서를 문서화해두는 것이 좋습니다.

🧮 3. 대용량 데이터에서는 chunk 단위로 처리

수백만 건 이상의 CSV를 한 번에 메모리에 로드하기 어렵다면, pandas의 read_csv(…, chunksize=) 옵션을 이용해 데이터를 나누어 처리하는 방법이 효과적입니다.

CODE BLOCK
import pandas as pd

chunks = pd.read_csv("bigdata.csv", chunksize=100000)
result = pd.concat([chunk.drop_duplicates(subset=["id"]) for chunk in chunks])

이 방식은 대용량 로그나 사용자 이벤트 데이터를 처리할 때 매우 유용합니다.
특히 클라우드 환경(Google Colab, AWS Lambda 등)에서는 메모리 제약이 있을 수 있으므로 chunk 기반 접근이 안전합니다.

🧠 4. 중복 탐지를 시각적으로 확인하기

중복을 제거하기 전, 어떤 행이 실제로 중복되는지 시각적으로 점검하면 실수를 줄일 수 있습니다.
특히 Jupyter Notebook 환경에서는 duplicated() 마스크를 이용해 중복 행만 따로 표시하는 것이 좋습니다.

CODE BLOCK
dups = df[df.duplicated(subset=["user_id", "event"], keep=False)]
display(dups.sort_values("user_id"))

💎 핵심 포인트:
성능을 높이려면 데이터 타입을 최적화하고, 처리 순서를 명확히 하며, 대용량은 chunk 방식으로 나누어 처리하세요.
무작정 drop_duplicates()만 실행하는 것보다, 사전 점검이 훨씬 빠르고 안전합니다.

  • ⚙️중복 제거 전 열 타입을 astype()으로 최적화
  • 📚중복 제거 순서를 명확히 정의
  • 💾대용량 데이터는 chunk 단위로 병합 처리
  • 🧠중복 탐지 후 drop 전에 눈으로 확인

자주 묻는 질문 (FAQ)

duplicated와 drop_duplicates의 차이는 무엇인가요?
duplicated()는 중복 여부를 True/False로 반환하는 반면, drop_duplicates()는 실제로 중복 행을 제거한 새로운 DataFrame을 반환합니다.
즉 duplicated()는 검사, drop_duplicates()는 정리 단계입니다.
subset 옵션을 생략하면 어떤 기준으로 중복을 판단하나요?
subset을 지정하지 않으면 pandas는 모든 컬럼 조합을 기준으로 중복을 판단합니다.
즉, 한 행의 모든 값이 완전히 같을 때만 중복으로 인식됩니다.
keep=False 옵션을 사용하면 어떤 결과가 나오나요?
동일한 값이 여러 번 나타나면 모두 중복으로 표시되며, drop_duplicates에서는 완전히 삭제됩니다.
즉, 유일하게 한 번만 등장한 값만 남게 됩니다.
inplace=True는 언제 사용하는 게 좋을까요?
데이터 크기가 크지 않거나, 별도의 백업이 필요 없는 단순 정리 단계에서 사용합니다.
다만 원본이 변경되므로 테스트 단계에서는 항상 inplace=False로 두고 결과를 검증한 후 사용하는 것이 좋습니다.
대용량 CSV에서 drop_duplicates가 너무 느립니다. 해결 방법이 있나요?
read_csv에 chunksize를 지정해 데이터를 나눠서 처리하거나, 중복 기준 컬럼만 추출한 후 dtype을 category로 바꿔 메모리를 절약하세요.
또한 Dask, Polars 등 병렬 연산 라이브러리를 활용하는 것도 좋은 방법입니다.
중복 제거 후 인덱스가 꼬였어요. 어떻게 복구하나요?
drop_duplicates는 행을 제거하기 때문에 기존 인덱스가 불연속적으로 남을 수 있습니다.
이때는 reset_index(drop=True)를 추가해 인덱스를 다시 정렬하면 됩니다.
NaN이 포함된 데이터는 중복으로 처리되나요?
pandas는 NaN을 서로 다른 값으로 취급하기 때문에 중복으로 인식하지 않습니다.
NaN을 동일하게 보고 싶다면 fillna()로 같은 값으로 채운 뒤 중복 검사를 수행하세요.
duplicated는 행뿐만 아니라 열(column)에도 적용할 수 있나요?
기본적으로 행 단위 중복만 지원합니다.
열 중복을 찾으려면 DataFrame.T로 전치(transpose)한 후 duplicated()를 적용하면 됩니다.

📊 pandas 중복 제거 핵심 정리와 활용 포인트

pandas에서 데이터 중복을 정리하는 방법은 단순히 함수를 외우는 수준을 넘어, 중복의 정의를 올바르게 설정하는 데서 출발합니다.
duplicated()로 중복 여부를 미리 확인하고, drop_duplicates()로 실제 정리를 수행하는 것이 기본 흐름입니다.
이때 subset은 비교 기준을, keep은 남길 값을 결정하며, 이 두 가지를 정확히 이해하면 어떤 형태의 데이터에서도 안정적으로 중복을 제거할 수 있습니다.

실무에서는 데이터 크기와 특성에 따라 방법을 달리해야 합니다.
데이터가 크다면 chunksize로 나누어 처리하고, 데이터 타입을 category로 최적화하며, 정렬 기준을 명시해 최신 또는 최초 데이터를 남길 수 있습니다.
결과적으로 중복 제거는 단순한 정리 작업이 아니라, 분석의 정확도와 속도를 결정하는 품질 관리 단계라고 할 수 있습니다.


🏷️ 관련 태그 : pandas, 데이터정리, duplicated, drop_duplicates, subset, keep, 데이터중복제거, 파이썬데이터분석, 데이터클렌징, 데이터프레임