본문 바로가기
두두 IT/파이썬

[PYTHON-Pandas 04-01]데이터프레임 합치기 (concat, join, merge)

by DoDo's 2026. 5. 6.
반응형

🐼 파이썬 Pandas 완벽 가이드: 데이터프레임 합치기 (concat, join, merge)

데이터 분석을 하다 보면, 여러 군데 흩어져 있는 데이터를 하나로 모아야 할 때가 정말 많습니다. 연도별로 나뉜 매출 데이터를 하나로 합치거나, 고객 정보와 주문 정보를 연결하는 등의 작업이죠.

Pandas에서는 두 개 이상의 DataFrame을 합쳐 하나의 DataFrame으로 만드는 강력한 기능들을 제공합니다. 오늘은 이 결합 방법들에 대해 완벽하게 마스터해 보겠습니다!


📖 1. 본격적인 시작 전, 필수 용어 정리

  • 수직 결합 (단순 결합): 여러 데이터프레임을 위아래(행 방향)로 길게 이어 붙이는 것입니다. (예: 2016년 데이터 밑에 2017년 데이터 붙이기). SQL의 UNION과 같은 역할을 합니다.
  • 수평 결합 (단순 결합 및 JOIN): 여러 데이터프레임을 좌우(열 방향)로 넓게 이어 붙이는 것입니다. (예: 직원 정보 옆에 부서 정보 붙이기). 특정 기준(컬럼이나 인덱스)이 같은 행끼리 묶어주는 것을 JOIN이라고 합니다.
  • Inner Join (교집합): 두 데이터프레임에 모두 존재하는 데이터만 합쳐서 가져옵니다.
  • Outer Join (합집합): 어느 한 쪽에만 데이터가 있어도 모두 가져옵니다.
    • Left Outer Join: 왼쪽 데이터를 무조건 살리고, 오른쪽 데이터를 끼워 맞춥니다.
    • Right Outer Join: 오른쪽 데이터를 무조건 살리고, 왼쪽 데이터를 끼워 맞춥니다.
    • Full Outer Join: 양쪽 데이터를 하나도 빠짐없이 모두 가져옵니다.

📂 2. 데이터셋 한 번에 읽어오기 (glob 모듈)

💡 이론

수십 개의 엑셀이나 CSV 파일을 하나씩 pd.read_csv()로 읽어오는 것은 비효율적입니다. 이때 파이썬의 glob 모듈을 사용합니다. glob 모듈은 파일 경로 내에서 패턴 매칭을 사용해 특정 파일이나 디렉토리를 쉽게 검색할 수 있도록 도와줍니다. 이때 *는 '0개 이상의 모든 문자'를 의미하며, 는 모든 하위 디렉토리를 의미합니다.

 

💻 코드 및 설명

Python
 
import pandas as pd
from glob import glob

# [1] glob을 사용하여 'data' 폴더 안의 's'로 시작하는 모든 csv 파일 경로를 리스트로 가져옵니다.
# 사용하는 이유: 파일이 수십 개일 때 일일이 파일명을 타이핑하는 수고를 덜기 위함입니다.
file_names = glob("data/s*.csv")

# [2] 리스트 컴프리헨션(List Comprehension)을 사용하여 여러 파일을 한 번에 읽어옵니다.
# 읽어온 데이터프레임들을 각각 stock_2016, stock_2017, stock_2018, stock_info 변수에 순서대로 할당합니다.
stock_2016, stock_2017, stock_2018, stock_info = [pd.read_csv(file_name) for file_name in file_names]

🧱 3. 무식하지만 확실하게 이어 붙이기: concat()

💡 이론

concat()은 수직 결합과 수평 결합을 모두 지원합니다. 주로 하나의 큰 데이터를 연도별/월별로 쪼개 놓은 것을 다시 하나로 합칠 때 사용합니다.

  • 수직 결합 (axis=0): 컬럼명이 같은 열끼리 밑으로 합칩니다. 만약 한쪽에만 있는 컬럼이라도 빈칸(NaN)으로 채워 넣고 결과에 포함시킵니다 (Full Outer Join 개념).
  • 수평 결합 (axis=1): 인덱스(index)가 같은 행끼리 옆으로 합칩니다.
  • 합칠 대상을 리스트 [df1, df2] 형태로 전달해야 합니다.

💻 코드 및 설명

Python
 
# [1] 수직으로 단순 결합 (행이 늘어남)
# axis=0은 기본값이므로 생략 가능합니다. 컬럼명이 같은 것끼리 위아래로 붙습니다.
result = pd.concat([stock_2016, stock_2017]) 

# [2] 수평으로 단순 결합 (열이 늘어남)
# axis=1을 주면 인덱스가 같은 것끼리 좌우로 붙습니다.
result_horizontal = pd.concat([stock_2016, stock_2017], axis=1)

# [3] 기존 인덱스 무시하고 새로 번호 부여하기 (ignore_index)
# 수직 결합을 하면 기존 데이터프레임의 인덱스 번호(0,1,2..)가 그대로 유지되어 인덱스가 중복될 수 있습니다.
# ignore_index=True를 주면 합친 대상의 기존 인덱스들을 무시하고 0부터 새롭게 인덱스를 쫙 부여합니다.
result2 = pd.concat(
    [stock_2016, stock_2017, stock_2018], 
    ignore_index=True 
)

# [4] 데이터 출처 표시하기 (keys)
# 데이터가 합쳐졌을 때, 이 데이터가 원래 어느 데이터프레임(ex: 몇 년도)에서 왔는지 출처를 다중 인덱스로 남길 때 사용합니다.
result3 = pd.concat(
    [stock_2016, stock_2017, stock_2018], 
    keys=['2016년', '2017년', '2018년'] 
)

# 다중 인덱스 조회 방법: '2017년'에 해당하는 데이터만 쏙 빼서 볼 수 있습니다.
result3.loc['2017년'] 

🤝 4. 2개 이상의 데이터를 인덱스 기준으로 합치기: join()

💡 이론

join()은 2개 이상의 DataFrame을 동시에 조인할 때 아주 유용합니다. 명심해야 할 특징은 무조건 '인덱스(index) 이름'을 기준으로 같은 행끼리 합친다는 점입니다. 기본 동작 방식은 왼쪽 데이터를 기준으로 하는 Left Outer Join입니다. 만약 결합하려는 데이터프레임들에 똑같은 이름의 컬럼이 존재한다면 에러가 발생하므로, lsuffix(왼쪽 꼬리표), rsuffix(오른쪽 꼬리표)를 달아 구별해 주어야 합니다.

 

💻 코드 및 설명

Python
# [1] 접미어(suffix) 붙이며 조인하기
# 두 데이터프레임에 중복되는 이름의 컬럼이 있을 때, 왼쪽(stock_info) 컬럼 이름 뒤에 "_info"를 붙여 에러를 방지합니다.
stock_info.join(stock_2016, lsuffix="_info") 

# [2] 인덱스를 맞춰서 조인하기 (핵심!)
# join()은 무조건 인덱스를 기준으로 합치기 때문에, 조인의 기준이 될 특정 컬럼('Symbol')을 set_index()로 인덱스로 만들어 준 뒤 합쳐야 합니다.
# how="inner"를 통해 두 데이터프레임에 공통으로 있는 'Symbol'만 남깁니다.
stock_info.set_index('Symbol').join(
    stock_2016.set_index('Symbol'), 
    how="inner" 
)

# [3] 여러 개의 DataFrame 한 번에 조인하기
# 2016, 2017, 2018 데이터를 헷갈리지 않게 각각 add_suffix()로 꼬리표를 달고 변수에 저장합니다.
s_2016 = stock_2016.set_index('Symbol').add_suffix('_2016')
s_2017 = stock_2017.set_index('Symbol').add_suffix('_2017')
s_2018 = stock_2018.set_index('Symbol').add_suffix('_2018')

# stock_info를 기준으로 3개의 데이터프레임을 리스트로 묶어 한 번에 싹 조인합니다.
join_result = stock_info.set_index('Symbol').join([s_2016, s_2017, s_2018])

🎯 5. 컬럼을 기준으로 정교하게 합치기: merge()

💡 이론

merge()는 오직 2개의 DataFrame만 합칠 수 있습니다. 하지만 join()보다 컨트롤이 훨씬 편하고 강력합니다. join()이 인덱스만 고집했다면, merge()는 컬럼과 컬럼, 혹은 컬럼과 인덱스 등 조인 기준을 아주 다양하게 설정할 수 있습니다. merge()의 기본 방식은 Inner Join(교집합)입니다.

 

💻 코드 및 설명

Python
# [1] 기본 merge 동작 (알아서 공통 컬럼 찾기)
# 두 데이터프레임에 공통으로 존재하는 컬럼명(ex: Symbol)을 자동으로 찾아 교집합(inner join)으로 합칩니다.
stock_info.merge(stock_2016)  

# [2] 기준 컬럼 명시하기 (on)
# 여러 개의 공통 컬럼이 있을 때, 어떤 컬럼을 기준으로 합칠지 명시적으로 지정합니다. 가장 권장되는 방식입니다.
stock_2016.merge(stock_2018, on="Symbol")

# [3] 왼쪽, 오른쪽 컬럼 이름이 서로 다를 때 조인하기 (left_on, right_on)
# 데이터는 같지만 컬럼명이 다를 수 있습니다. (예: 왼쪽은 'Symbol_2018', 오른쪽은 'Symbol')
# 이때는 각각 어떤 컬럼을 기준으로 매칭할지 친절하게 알려주어야 합니다.
s_2018_2.merge(stock_info, left_on='Symbol_2018', right_on='Symbol')

# [4] 한쪽은 컬럼, 한쪽은 인덱스를 기준으로 조인하기
# 왼쪽 데이터프레임(stock_info)은 'Symbol' 컬럼을 사용하고, 
# 오른쪽 데이터프레임(s_2016)은 설정된 인덱스를 사용해서 조인하라는 뜻입니다.
stock_info.merge(s_2016, left_on="Symbol", right_index=True)

# [5] 중복 컬럼 이름 변경하기 (suffixes)
# 두 데이터프레임에 공통된 컬럼명이 있으면 조인 후 이름 뒤에 _x, _y가 지저분하게 붙습니다.
# 이를 방지하기 위해 suffixes=['_2016', '_2018']로 우리가 원하는 깔끔한 꼬리표를 달아줍니다.
stock_2016.merge(stock_2018, on="Symbol", suffixes=['_2016', '_2018'])

📝 총정리 요약

  • 단순히 위아래로 붙일 때 (수직 결합) ➡️ 무조건 concat() 사용.
  • 3개 이상의 데이터를 한 번에 조인할 때 ➡️ join() 사용.
  • 2개의 데이터를 디테일한 컬럼 조건으로 조인할 때 ➡️ merge() 사용 (가장 많이 쓰고 컨트롤이 편함).

🛠️ 오늘의 Todo 연습 문제!

여러분이 방금 배운 개념을 점검할 수 있는 문제입니다. 직접 코드를 고민해 보세요!

[상황]

우리 회사에 직원들의 기본 정보를 담은 employees 데이터프레임과, 직원들의 연봉 정보를 담은 salaries 데이터프레임이 있습니다.

  1. employees 데이터프레임에는 직원 고유 번호인 emp_id 컬럼이 있습니다.
  2. salaries 데이터프레임에는 직원 고유 번호인 id 컬럼이 있습니다. (이름만 다를 뿐 내용은 같습니다!)

[문제]

이 두 데이터프레임을 직원 고유 번호를 기준으로 모든 직원의 정보가 누락되지 않게(Full Outer Join) 합치려고 합니다. 오직 두 개의 데이터프레임만 결합하며, 컬럼을 기준으로 결합해야 합니다.

어떤 메서드를 써야 하며, 코드를 어떻게 작성해야 할까요?

(힌트: 결합 메서드 선택 ➡️ 왼쪽 컬럼 기준 지정 ➡️ 오른쪽 컬럼 기준 지정 ➡️ 조인 방식 지정)

반응형