파이썬 pandas merge 후 행이 갑자기 늘어나는 이유와 validate 사용법

pandas merge 후 행이 늘어나는 가장 흔한 이유는 양쪽 데이터의 같은 키가 여러 번 나타나 many-to-many 관계가 되고, 각 조합이 결과에 반영되기 때문입니다. 따라서 결과를 줄이기 전에 양쪽 키의 중복 여부와 의도한 병합 관계부터 확인해야 합니다. 이 글에서는 validate로 관계를 검사하고 indicator=True로 매칭 상태를 확인하는 순서를 정리하겠습니다.

먼저 확인할 것: 병합 키는 정말 유일한가

예를 들어 고객 목록과 주문 요약을 customer_id로 합친다고 하겠습니다. 고객 쪽에 같은 ID가 두 행, 주문 쪽에도 같은 ID가 세 행 있으면 해당 ID는 결과에서 여섯 조합으로 나타날 수 있습니다. 이것은 merge가 임의로 중복을 만든 것이 아니라, 두 데이터의 관계를 그대로 확장한 결과입니다.

병합 전에 다음처럼 키의 중복을 별도로 확인하면 원인을 빠르게 좁힐 수 있습니다.

import pandas as pd

left_key = "customer_id"
right_key = "customer_id"

left_duplicates = left[left_key].duplicated(keep=False)
right_duplicates = right[right_key].duplicated(keep=False)

print(left.loc[left_duplicates, [left_key]])
print(right.loc[right_duplicates, [right_key]])

중복이 발견되면 먼저 이것이 데이터 오류인지, 실제로 한 고객에 여러 주문처럼 허용된 관계인지 판단해야 합니다. 중복 행을 무조건 제거하면 집계 대상 자체를 잃을 수 있습니다.

validate로 기대한 관계를 계약으로 남기기

관계를 알고 있다면 merge에 validate를 지정하는 편이 안전합니다. 양쪽 키가 모두 유일해야 한다면 validate="1:1", 왼쪽 키는 유일하고 오른쪽에서 여러 행을 허용한다면 validate="1:m", 오른쪽 키만 유일해야 한다면 validate="m:1"을 사용합니다.

merged = left.merge(
    right,
    left_on="customer_id",
    right_on="customer_id",
    how="left",
    validate="1:m",
)

예제의 1:m은 왼쪽의 한 키가 오른쪽의 여러 행과 연결될 수 있다는 뜻입니다. 반대로 왼쪽 키도 유일해야 하는데 중복이 들어오면 병합 결과를 계속 사용하는 대신 예외를 확인하고 원천 데이터를 정리하는 흐름으로 바꿀 수 있습니다. validate의 값은 데이터 정제 명령이 아니라, 병합 전에 세운 관계가 맞는지 검사하는 장치입니다.

indicator=True로 누락된 매칭 찾기

행 수가 기대와 다를 때는 중복뿐 아니라 한쪽에만 존재하는 키도 확인해야 합니다. indicator=True를 지정하면 결과에 _merge 열이 추가되고 각 행의 매칭 상태를 구분할 수 있습니다.

checked = left.merge(
    right,
    on="customer_id",
    how="outer",
    indicator=True,
)

not_matched = checked[checked["_merge"] != "both"]
print(not_matched)

left_only는 왼쪽에만 있는 키, right_only는 오른쪽에만 있는 키, both는 양쪽에 연결된 키를 뜻합니다. how="outer"를 사용하면 어느 쪽에서 누락되었는지 함께 볼 수 있고, 실제 저장용 결과에는 업무 목적에 맞는 how를 따로 선택하면 됩니다.

null 키는 별도로 점검하기

일반적인 SQL의 null 조인과 같은 방식이라고 생각하면 놓치기 쉬운 부분도 있습니다. pandas merge에서는 양쪽 키가 null인 경우 서로 매칭될 수 있으므로, null 키가 있는지 먼저 확인하고 업무적으로 같은 미상 항목을 연결해도 되는지 결정해야 합니다.

print(left[left["customer_id"].isna()])
print(right[right["customer_id"].isna()])

null을 임시 값으로 치환할 때는 실제 ID와 충돌하지 않는지, null 행을 제외할 때는 제외가 업무 규칙에 맞는지 확인해야 합니다. 중요한 것은 pandas의 기본 동작을 추측하지 않고 입력 데이터의 상태와 원하는 결과를 함께 명시하는 것입니다.

merge와 concat을 혼동하지 않기

공통 키를 기준으로 관련 행을 연결하려는 목적이면 merge가 적합합니다. 반면 같은 열 구조의 데이터를 행 방향으로 이어 붙이거나, 서로 다른 열을 축 기준으로 결합하려는 목적이면 concat을 검토합니다. 파일별로 받은 동일한 형식의 데이터를 모아 한 목록으로 만드는 작업을 키 조인으로 처리하면, 의도하지 않은 행 확장이 생길 수 있습니다.

운영 전에 확인하는 순서

저라면 다음 순서로 점검합니다.

  1. left와 right의 병합 키에 중복과 null이 있는지 확인합니다.
  2. one-to-one, one-to-many 등 실제로 허용할 관계를 정합니다.
  3. 그 관계에 맞는 validate를 지정해 병합 계약을 코드에 남깁니다.
  4. 필요하면 indicator=True와 how="outer"로 누락 키를 확인합니다.
  5. 마지막으로 병합 전후 행 수와 주요 키의 분포를 비교한 뒤 저장이나 집계를 진행합니다.

행이 늘었다는 사실만으로 오류라고 단정할 필요는 없습니다. 다만 예상하지 못한 many-to-many 관계라면 집계값과 후속 저장 결과가 달라질 수 있으므로, 중복 키를 제거할지 1:N 관계로 허용할지 먼저 결정해야 합니다. 이 판단이 끝난 뒤에야 merge를 유지할지 concat으로 바꿀지, 또는 원천 데이터를 정리할지 선택할 수 있습니다.

참고 자료

Leave a Comment