[판다스 19화] merge, join, concat 차이점과 데이터 병합 방법 | Python Pandas | 빅분기, 빅데이터분석기사 실기

파이썬(Python) 판다스(Pandas) 라이브러리를 활용해 실무 데이터 분석이나 머신러닝 모델링을 진행하다 보면, 하나의 데이터프레임만으로는 원하는 결론을 도출하기 어려울 때가 많습니다. 기업의 데이터베이스(RDBMS) 구조처럼 고객 정보 테이블, 주문 이력 테이블, 상품 마스터 테이블 등이 각각 분산되어 저장되어 있기 때문입니다.

이처럼 여러 출처에서 수집된 복수의 데이터프레임을 하나로 결합하여 결합 데이터를 생성하는 과정을 ‘데이터 병합(Data Merging)’ 또는 ‘결합(Join)’이라고 부릅니다. 판다스는 이를 처리하기 위해 merge(), join(), concat()이라는 세 가지 핵심 함수를 제공합니다.

세 함수는 데이터를 하나로 합친다는 목적은 같지만, 데이터를 연결하는 기준축(행 인덱스 기준인가, 특정 컬럼 기준인가)과 작동 메커니즘에 뚜렷한 차이가 있습니다. 이를 명확히 구분하지 못하면 데이터가 누락되거나 중복 행이 복사되는 논리적 오류가 발생할 수 있습니다. 이번 글에서는 세 함수의 차이점과 구체적인 활용법을 체계적으로 정리해 보겠습니다.

1. 관계형 데이터베이스 스타일의 열 기준 병합: merge() 함수

merge() 함수는 두 데이터프레임의 공통된 특정 열(Column)을 기준으로 삼아, SQL의 JOIN 문법과 유사하게 가로 방향으로 테이블을 결합하는 가장 범용적인 도구입니다.

1.1. merge()의 핵심 파라미터 구조

  • on: 병합의 기준이 될 공통 컬럼명을 지정합니다. 양쪽 데이터프레임의 기준 컬럼명이 같다면 on='공통컬럼명'으로 선언합니다.
  • left_on / right_on: 왼쪽 데이터프레임과 오른쪽 데이터프레임의 기준 컬럼명이 서로 다를 때 각각 명시적으로 지정하는 파라미터입니다.
  • how: 병합의 방식을 결정하며, 크게 4가지 옵션이 존재합니다.
    • 'inner': 양쪽 데이터프레임에 모두 존재하는 기준값만 남깁니다. (기본값)
    • 'left': 왼쪽 데이터프레임의 모든 행을 유지하고, 오른쪽 데이터프레임은 일치하는 값만 붙입니다. (일치하지 않는 빈칸은 NaN 처리)
    • 'right': 오른쪽 데이터프레임의 모든 행을 유지하고, 왼쪽 데이터프레임은 일치하는 값만 붙입니다.
    • 'outer': 양쪽 데이터프레임 중 한 곳에라도 존재하는 모든 행을 합집합 형태로 보존합니다.

1.2. 비즈니스 예시로 이해하는 merge 활용

가상의 고객 마스터 테이블과 당일 주문 테이블을 생성하여 회원ID를 기준으로 결합해 보겠습니다.

import pandas as pd

# 1. 고객 데이터 (Left DataFrame)
user_df = pd.DataFrame({
    '회원ID': ['U01', 'U02', 'U03', 'U04'],
    '이름': ['강민준', '서연우', '김도현', '박지아']
})

# 2. 주문 데이터 (Right DataFrame)
order_df = pd.DataFrame({
    '주문번호': [1001, 1002, 1003],
    '회원ID': ['U01', 'U02', 'U05'],
    '주문액': [50000, 120000, 35000]
})

# 회원ID를 기준으로 Left Join 수행
merged_result = pd.merge(user_df, order_df, on='회원ID', how='left')
print(merged_result)

[출력 결과]

  회원ID   이름    주문번호      주문액
0   U01  강민준  1001.0  50000.0
1   U02  서연우  1002.0 120000.0
2   U03  김도현     NaN      NaN
3   U04  박지아     NaN      NaN

결과를 살펴보면 how='left' 기준에 따라 왼쪽 user_df에 있던 4명의 고객 정보가 유지되었습니다. 오늘 주문 이력이 없는 ‘U03’, ‘U04’ 고객의 주문번호와 주문액 칸에는 결측치(NaN)가 안전하게 대입되어 통합 회원 테이블이 완성되었습니다. 주문 데이터에 존재하던 비회원 혹은 데이터 유실 건인 ‘U05’ 행은 레프트 조인 특성상 결과에서 제외되었습니다.

2. 행 인덱스 중심의 간편한 결합: join() 메서드

join() 메서드는 데이터프레임(DataFrame)에서 제공하는 객체 메서드로, merge()와 유사한 기능을 수행하며 주로 행 인덱스(Index)를 기준으로 데이터를 결합할 때 사용합니다.

2.1. join()의 특징과 구동 방식

기본적으로 df_left.join(df_right) 형태로 사용하며, how 파라미터의 기본값이 'left'라는 점에서 merge(how='inner')와 차이가 있습니다.

# 인덱스를 회원ID로 설정한 가상 데이터
user_indexed = user_df.set_index('회원ID')
order_indexed = order_df.set_index('회원ID')

# 행 인덱스 기준으로 테이블 조인 수행 (기본값 left)
join_result = user_indexed.join(order_indexed, lsuffix='_left', rsuffix='_right')
  • lsuffix / rsuffix: 양쪽 데이터프레임에 동일한 이름의 컬럼이 존재하는 경우 컬럼명 충돌을 방지하기 위해 접미사를 추가합니다.

3. 축을 따른 단순 이어 붙이기: concat() 함수

merge()join()이 특정 키(Key)를 맵핑하여 논리적으로 연결하는 방식이라면, concat() (Concatenate) 함수는 물리적으로 데이터프레임을 축(Axis)에 따라 그대로 이어 붙이는 도구입니다. 주로 구조가 동일한 여러 기간의 매출 데이터를 하나로 통합할 때 사용됩니다.

3.1. 세로 방향 연결 (axis=0)

axis=0은 위아래(행 방향)로 데이터를 연결하는 방식이며, concat()의 기본 작동 방식입니다.

# 상반기 데이터와 하반기 데이터 생성
h1_df = pd.DataFrame({'상품': ['A', 'B'], '실적': [100, 150]})
h2_df = pd.DataFrame({'상품': ['C', 'D'], '실적': [200, 110]})

# 두 테이블을 세로로 연결하고 인덱스 재정돈
annual_df = pd.concat([h1_df, h2_df], axis=0, ignore_index=True)
  • ignore_index=True: 데이터프레임을 위아래로 이어 붙이면 기존 데이터의 인덱스 번호(0, 10, 1)가 그대로 유지되어 중복 인덱스가 발생합니다. 이 파라미터를 True로 지정하면 기존 인덱스를 무시하고 0부터 새로 시작하는 일련번호로 깔끔하게 초기화해 줍니다.

3.2. 가로 방향 연결 (axis=1)

axis=1을 지정하면 같은 인덱스를 가진 행끼리 열 방향으로 결합됩니다. 행의 인덱스 번호가 일치하는 원소끼리 단순 수평 결합하게 됩니다.

4. merge, join, concat 핵심 차이점 비교 요약

세 함수의 용도와 기준축의 차이를 표로 정리하면 다음과 같습니다.

함수/메서드 명결합 방향기본 결합 기준 (Key)디폴트 조인 방식 (how)주요 활용 시나리오
pd.merge()가로 (열 확장)특정 공통 컬럼 (값 매칭)'inner' (교집합)SQL 방식의 관계형 테이블 결합
df.join()가로 (열 확장)행 인덱스 (Index 명칭)'left' (왼쪽 우선)인덱스가 이미 정렬된 테이블 간의 빠른 조인
pd.concat()세로 또는 가로선택 축 (axis=0 또는 1)'outer' (합집합)동일한 컬럼 구조를 가진 기간별 데이터 적재

5. 실무 환경에서의 데이터 병합 시 유의사항 및 예외 처리

5.1. 병합 후 발생하는 중복 컬럼의 접미사 관리 (suffixes)

merge()를 수행할 때 기준 컬럼 외에 다른 컬럼의 이름이 양쪽 데이터프레임에 모두 존재하면, 판다스는 결과 데이터프레임에서 두 열을 구분하기 위해 자동으로 이름 뒤에 _x, _y라는 접미사를 붙입니다. 이 표현을 비즈니스 명칭에 맞게 직관적으로 변경하고 싶다면 suffixes 파라미터를 명시적으로 선언하는 편이 좋습니다.

# 중복 열 이름 뒤에 지정한 접미사 매핑
final_df = pd.merge(user_df, order_df, on='회원ID', how='inner', suffixes=('_고객', '_주문'))

5.2. 결합 데이터 타입 불일치 오류 방지

merge() 연산을 수행했음에도 불구하고 데이터가 정상적으로 매칭되지 않고 빈 테이블이 반환되거나 ValueError가 발생하는 주된 원인 중 하나는 기준 컬럼의 데이터 타입 불일치입니다. 예를 들어 한쪽 테이블의 회원ID는 정수형(101)인데 다른 쪽은 문자열('101')로 저장되어 있다면 컴퓨터는 이를 서로 다른 데이터로 취급합니다.

따라서 병합 전에 dtypes 속성이나 info()를 통해 병합 기준 변수의 데이터 타입을 일치시켜 주는 사전 정제 작업이 선행되어야 안전합니다.

6. 빅데이터분석기사 실기 시험 환경에서의 실전 활용 포인트

빅데이터분석기사 실기 시험(특히 작업형 제2유형인 머신러닝 모델링)에서는 데이터셋이 처음부터 훈련용 독립변수(X_train.csv)와 종속변수(y_train.csv)로 분리되어 제공되는 경우가 대다수입니다.

6.1. 학습 데이터 병합과 전처리

특징 엔지니어링이나 데이터 전처리를 수행할 때, 행의 순서가 흐트러지지 않도록 보장하면서 독립변수와 종속변수를 결합해 다루어야 안정적인 전처리가 가능할 때가 있습니다. 이때 고유 식별 번호(예: Cust_ID)를 키값으로 삼아 merge() 처리를 수행하는 워크플로우를 주로 활용합니다.

# [시험장 실전 팁]
# 1. 분리된 Train 데이터셋의 ID 기준 병합
full_train = pd.merge(X_train, y_train, on='Cust_ID', how='inner')

# 2. 통합된 데이터에서 이상치 제거 및 파생변수 생성 전처리 일괄 수행
# ... [전처리 코드 작성] ...

# 3. 모델 훈련 직전 다시 독립변수와 타깃 변수로 분리
X_train_clean = full_train.drop(columns=['Cust_ID', 'Target_Column'])
y_train_clean = full_train['Target_Column']

시험 데이터 중에는 결측치나 누락 행이 포함될 수 있으므로, 행 수가 손실되는 현상을 막기 위해 how 파라미터 설정을 신중하게 검토해야 합니다. 데이터프레임을 정확하게 병합하고 분리하는 능력은 데이터 품질을 유지하고 안정적인 모델 학습을 수행하는 데 중요한 기반이 됩니다.

댓글 남기기