본문으로 건너뛰기
김신건의 로그

[Pandas] concat

· 수정 · 📖 약 2분 · 667자/단어 #python #pandas #concat #reshape
Pandas concat, pd.concat, pandas append, DataFrame concat, Series concat

정의

pd.concat(objs, axis=0) 는 여러 DataFrame/Series 를 세로 (axis=0) 또는 가로 (axis=1) 로 단순 연결. SQL 의 UNION (axis=0) 또는 column-level concatenation (axis=1).

key 매칭이 아닌 위치 기반 결합 이라 Pandas merge / Pandas join 과 구분된다.

사용 상황

  • 행 단위 쌓기: 같은 구조의 DataFrame 여러 개를 하나로 합칠 때
  • 가로 붙이기: 같은 index 를 공유하는 feature 그룹을 열 방향으로 결합할 때
  • 계층 라벨 부여: 시기/그룹별 데이터를 묶어 Pandas MultiIndex 로 분석할 때
  • df.append 대체: pandas 2.0 에서 append 가 제거됨, pd.concat 으로 완전 대체

merge / join 과 달리 key 매칭이 없으므로, 스키마가 같거나 의도적으로 다른 경우에 쓴다.

axis 동작 시각화

flowchart TD
    subgraph "axis=0, 세로 연결"
        A1["df1: 2행"] --> R1["결과: 4행 쌓기"]
        A2["df2: 2행"] --> R1
    end

    subgraph "axis=1, 가로 연결"
        B1["df1: 2열"] --> R2["결과: 4열 붙이기"]
        B2["df2: 2열"] --> R2
    end

join 파라미터 시각화

flowchart LR
    subgraph "join='outer' 기본"
        O1["df1: 열 a, b"] --> OR["결과: a, b, c 합집합 / NaN 채움"]
        O2["df2: 열 a, c"] --> OR
    end

    subgraph "join='inner'"
        I1["df1: 열 a, b"] --> IR["결과: a 교집합만"]
        I2["df2: 열 a, c"] --> IR
    end

세로 연결 (axis=0, 기본)

result = pd.concat([df1, df2])
python
import pandas as pd
df1 = pd.DataFrame({'name':['A','B'], 'age':[10,20]})
df2 = pd.DataFrame({'name':['C','D'], 'age':[30,40]})
print(pd.concat([df1, df2]))
결과
  name  age
0    A   10
1    B   20
0    C   30
1    D   40

index 가 중복됨 (0, 1, 0, 1). ignore_index=True 로 재설정:

pd.concat([df1, df2], ignore_index=True)
# index: 0, 1, 2, 3

가로 연결 (axis=1)

pd.concat([df1, df2], axis=1)
# 컬럼이 옆으로 붙음, index 기준 정렬
python
import pandas as pd
left = pd.DataFrame({'a': [1, 2, 3]}, index=[0, 1, 2])
right = pd.DataFrame({'b': [10, 20, 30]}, index=[0, 1, 2])
print(pd.concat([left, right], axis=1))
결과
   a   b
0  1  10
1  2  20
2  3  30

index 가 다르면 NaN 이 생긴다:

left = pd.DataFrame({'a': [1, 2]}, index=[0, 1])
right = pd.DataFrame({'b': [10, 20]}, index=[1, 2])
pd.concat([left, right], axis=1)
#     a     b
# 0  1.0   NaN
# 1  2.0  10.0
# 2  NaN  20.0

컬럼이 다를 때

df1 = pd.DataFrame({'a': [1,2], 'b': [3,4]})
df2 = pd.DataFrame({'a': [5,6], 'c': [7,8]})

pd.concat([df1, df2])
# a: 1,2,5,6
# b: 3,4,NaN,NaN
# c: NaN,NaN,7,8

기본 join='outer', 합집합. join='inner' 면 교집합 컬럼만:

pd.concat([df1, df2], join='inner')
# 공통 컬럼 a 만 남음

keys (계층적 라벨)

pd.concat([df1, df2], keys=['Q1', 'Q2'])
# 결과의 index 가 MultiIndex: (Q1, 0), (Q1, 1), (Q2, 0), (Q2, 1)

여러 시기/그룹의 데이터를 한 번에 모아 분석할 때 유용:

combined = pd.concat([q1, q2, q3], keys=['Q1', 'Q2', 'Q3'])

# 특정 분기만 추출
combined.loc['Q2']

# 분기별 집계
combined.groupby(level=0).sum()

names 파라미터로 MultiIndex 레벨 이름도 지정:

pd.concat(
    [df_2024, df_2025],
    keys=[2024, 2025],
    names=['year', 'original_idx'],
)

자주 쓰는 패턴

여러 파일을 하나로

import glob
dfs = [pd.read_csv(f) for f in glob.glob('data/*.csv')]
big = pd.concat(dfs, ignore_index=True)

새 행 추가 (pandas 2.x)

# pandas 1.x 의 df.append 는 pandas 2.0 에서 제거됨
new_row = pd.DataFrame([{'name': 'E', 'age': 50}])
df = pd.concat([df, new_row], ignore_index=True)

컬럼 추가 (가로 결합)

features = compute_features(df)  # 별도 feature DataFrame
result = pd.concat([df, features], axis=1)

딕셔너리에서 concat

frames = {'train': df_train, 'val': df_val, 'test': df_test}
combined = pd.concat(frames)
# MultiIndex: ('train', 0), ('val', 0), ...
combined.loc['train']   # 학습 데이터만 추출

조건부 분기 처리 후 재결합

premium = df[df['tier'] == 'premium'].copy()
standard = df[df['tier'] != 'premium'].copy()
premium['discount'] = 0.2
standard['discount'] = 0.05
result = pd.concat([premium, standard]).sort_index()

Nullable dtype 유지 (pandas 2.x)

# int 컬럼에 NaN 이 섞여도 float 강등을 방지
df1 = pd.DataFrame({'count': pd.array([1, 2], dtype='Int64')})
df2 = pd.DataFrame({'count': pd.array([None, 4], dtype='Int64')})
pd.concat([df1, df2], ignore_index=True)
# count 컬럼: Int64 (nullable) 유지

성능

concat전체 결과 메모리를 미리 할당 한 뒤 복사. 큰 데이터를 루프 안에서 반복 concat 하면 O(n²) 비용 가능.

# 느림: 루프 안에서 반복 concat
result = pd.DataFrame()
for df in dfs:
    result = pd.concat([result, df])

# 빠름: 한 번에 리스트 전달
result = pd.concat(dfs, ignore_index=True)

대용량 concat 에서 dtype 을 미리 지정하면 메모리 절약:

dtypes = {'id': 'int32', 'value': 'float32', 'label': 'category'}
dfs = [pd.read_csv(f, dtype=dtypes) for f in files]
result = pd.concat(dfs, ignore_index=True)

TIP

pandas 2.x 에서는 pd.concat 결과가 항상 새 메모리를 할당한다. 임시 리스트를 concat 직후 del dfs 로 해제하면 피크 메모리를 줄일 수 있다.

pandas 2.x 변경점

항목pandas 1.xpandas 2.x
df.append지원제거됨 (pd.concat 으로 대체)
sort 기본값FalseFalse (유지)
Copy-on-Write미지원기본 활성화
Nullable int + NaN concatfloat 강등Int64 유지 가능

merge / join / concat 비교

동작함수
key 매칭merge
index 매칭join
단순 연결 (위/옆)concat

함정

1. index 중복

pd.concat([df1, df2])
# 0, 1, 0, 1 -> .loc[0] 이 두 행 반환
# 해법: ignore_index=True 또는 reset_index(drop=True)

2. dtype 강등

# a 가 int 인 df1 에 NaN 이 포함된 df2 를 concat 하면 float 으로 강등
# 해법: Nullable dtype 사용
df1 = pd.DataFrame({'a': pd.array([1, 2], dtype='Int64')})
df2 = pd.DataFrame({'a': pd.array([None], dtype='Int64')})
pd.concat([df1, df2])  # Int64 유지

3. 컬럼 순서

# 합집합이지만 순서는 첫 DataFrame 우선
# df1 의 컬럼 + df2 의 새 컬럼 (뒤에 추가)
# 의도치 않은 순서를 원하면 .reindex(columns=원하는_순서) 로 정렬

4. axis=1 에서 index 불일치

left = pd.DataFrame({'a': [1, 2]}, index=[0, 1])
right = pd.DataFrame({'b': [10]}, index=[0])
pd.concat([left, right], axis=1)
# NaN 생성: right.b 의 index 1 은 NaN

WARNING

axis=1 concat 시 두 DataFrame 의 index 가 완전히 일치하지 않으면 NaN 이 생긴다. join='inner' 로 교집합 index 만 유지하거나, 사전에 index 를 정렬/재설정하라.

5. df.append() 제거 (pandas 2.0)

# AttributeError
df = df.append(new_row)

# 대체
df = pd.concat([df, new_row], ignore_index=True)

6. sort 파라미터

# pandas 2.x 기본: sort=False (첫 DataFrame 컬럼 순서 유지)
# sort=True 하면 알파벳 정렬
pd.concat([df1, df2], sort=False)

관련 위키

이 글의 용어 (8개)
[Pandas] .loc / .ilocpandas
정의 - : 라벨 기반 인덱싱 (index/column 이름) - : 정수 위치 기반 인덱싱 (0-based) 이 두 indexer 가 pandas 선택의 표준. 만으로는 헷갈리…
[Pandas] DataFramepandas
정의 은 2차원 레이블 테이블. 각 열이 , 모든 열이 같은 (행 라벨) 를 공유. SQL 테이블 / Excel 시트 / R data.frame 의 Python 대응체. 구조 시…
[Pandas] groupbypandas
정의 는 데이터를 그룹으로 나누고 (split), 각 그룹에 함수를 적용 (apply), 결과를 합쳐 (combine) 새 DataFrame 으로 만드는 split-apply-c…
[Pandas] joinpandas
정의 는 index 기반 결합 의 편의 API. 내부적으로는 와 동등하지만 더 짧다. pandas 에서 두 DataFrame 을 결합하는 세 가지 주요 함수: - : 컬럼 / 인…
[Pandas] mergepandas
정의 는 두 DataFrame 을 SQL join 처럼 결합. 4 가지 join 타입 (inner / left / right / outer) 지원. 사용 상황 - 두 테이블 키 …
[Pandas] MultiIndexpandas
정의 MultiIndex 는 여러 단계(level)의 라벨로 구성된 . SQL의 composite key와 비슷하며, 그룹화된 데이터를 자연스럽게 표현한다. , , 등이 자동으로…
[Pandas] Nullable Types (Int64, boolean, string[pyarrow])pandas
정의 Nullable dtypes 는 NaN/None 을 타입 그대로 표현할 수 있는 pandas 의 새 타입 시스템. NumPy 기반의 (NaN 미허용) 의 한계를 극복. - …
[Pandas] read_csv / to_csvpandas
정의 는 CSV(Comma-Separated Values) 또는 구분자 기반 텍스트 파일을 DataFrame 으로 읽는 함수. pandas 사용의 출발점. 반대 방향은 : Dat…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기