메뉴 닫기

파이썬 pandas stack unstack 가이드 – MultiIndex와 level로 스택 언스택 변환·정형화

파이썬 pandas stack unstack 가이드 – MultiIndex와 level로 스택 언스택 변환·정형화

🧭 스택과 언스택으로 표 형태를 재구성하고 MultiIndex까지 깔끔하게 다루는 실무 중심 비법을 소개합니다

엑셀처럼 보이는 데이터라도 분석 단계에 들어가면 열과 행의 축을 바꾸고 계층을 정리해야 할 때가 많습니다.
그럴 때 가장 탄탄한 도구가 바로 파이썬 pandas의 stackunstack입니다.
이 두 함수는 시계열 집계 테이블부터 로그성 데이터까지 형태를 자유자재로 전환해 주며, 피벗만으로는 해결되지 않는 복잡한 재배치를 간결한 한 줄로 처리하게 도와줍니다.
특히 멀티 인덱스 구조를 가진 데이터프레임에서 열과 행의 수준을 오가며 원하는 형태로 정형화하는 데 강력한 위력을 발휘합니다.
데이터가 커질수록 일관된 규칙으로 변환 과정을 관리하는 것이 중요하고, 오류 없이 반복 가능한 파이프라인을 만드는 데에도 스택·언스택은 핵심 도구가 됩니다.

이 글은 파이썬 pandas > 변환·정형화 > 스택/언스택을 주제로, stack/unstack(level)의 동작 원리와 활용 패턴을 체계적으로 정리합니다.
또한 MultiIndex와 결합했을 때 어떤 전략으로 열과 행의 계층을 다루는지, 피벗과 어떤 차이가 있는지까지 실무 흐름에 맞춰 설명합니다.
데이터 품질을 해치지 않으면서도 메모리 사용과 성능을 고려하는 팁, 실수하기 쉬운 예외 상황을 줄이는 점검 포인트도 함께 제공합니다.
초보자도 차근차근 따라갈 수 있도록 개념을 풀어내고, 숙련자에게는 유지보수 가능한 코드 스타일을 제안해 생산성을 높이는 데 도움을 드리겠습니다.



🔗 파이썬 pandas 스택 언스택 개념과 동작

pandas에서 stack은 열 축의 레이블을 행 축의 하위 수준으로 내려서 길게(long) 만드는 연산입니다.
반대로 unstack(level)은 행 축의 하위 수준을 꺼내 열 축으로 올려 넓게(wide) 재구성합니다.
즉, 두 연산은 서로 역연산 관계를 이루며, 같은 레벨을 기준으로 연속 적용하면 원형 복원이 가능합니다.
이 때 level 인자는 다층 인덱스에서 어느 수준을 이동시킬지 지정하는 핵심 매개변수로, 이름(label) 또는 정수 위치로 지정할 수 있습니다.
단일 인덱스에서도 동작하지만, 진가를 발휘하는 상황은 MultiIndex로 열이나 행이 계층화돼 있을 때입니다.

🧩 stack의 동작 원리와 반환 형태

stack은 열 인덱스의 마지막 수준을 행 인덱스의 가장 안쪽 수준으로 이동시킵니다.
그 결과 시리즈(Series) 또는 데이터프레임이 되며, 기본적으로 누락값은 결과에서 제외되어 더 컴팩트한 형태가 됩니다.
열에 다층 인덱스가 있으면 가장 오른쪽 수준부터 차례로 내려가며, level을 지정하면 특정 수준만 선택적으로 내릴 수 있습니다.
범주형, 시계열, 문자열 등 데이터 타입과 무관하게 작동하며, 그룹 집계나 피벗 뒤에 이어서 길게 변환할 때 자주 사용됩니다.

🧭 unstack(level)의 동작 원리와 반환 형태

unstack은 행 인덱스의 지정된 수준을 열로 끌어올려 넓은 테이블을 만듭니다.
지정한 수준이 가진 고유값이 새 열의 레벨이 되며, 조합이 존재하지 않는 셀은 기본적으로 NaN이 채워집니다.
숫자 분석 전에 가독성을 위해 단면을 넓게 펼치거나, 시각화 라이브러리와 호환되는 형태를 만들고 싶을 때 유용합니다.
또한 fill_value로 누락값을 즉시 대체하려면 unstack 이후 fillna를 연달아 적용하는 패턴이 일반적입니다.

CODE BLOCK
import pandas as pd

# 예제 데이터
df = pd.DataFrame({
    "지역": ["서울","서울","부산","부산"],
    "분기": ["Q1","Q2","Q1","Q2"],
    "매출": [120, 140, 80, 100]
})

# 넓은 형태로 만들기 (행 → 열로 펼치기)
wide = df.pivot(index="지역", columns="분기", values="매출")

# stack: 열의 마지막 수준(분기)을 행의 안쪽 수준으로 내리기 → 길게
long_by_stack = wide.stack()  # index: (지역, 분기), name: 매출

# unstack: 행의 지정 수준을 열로 올리기 → 넓게
wide_by_unstack = long_by_stack.unstack(level="분기")  # wide와 동일 구조

# MultiIndex 열에서 부분 stack
wide2 = pd.concat({"매출": wide, "수량": wide / 10}, axis=1)  # 열이 (지표, 분기) 구조
part_stack = wide2.stack(level="분기")  # '분기'만 내림 → index: (지역, 분기), columns: 지표

💡 TIP: 피벗 결과가 열 MultiIndex(예: (지표, 분기))라면, stack(level=”분기”)처럼 특정 수준만 선택적으로 이동시키면 분석 축을 깔끔하게 전환할 수 있습니다.

⚠️ 주의: unstack은 동일한 인덱스 조합이 중복되면 오류가 납니다.
먼저 groupby().sum() 등으로 고유 조합만 남기거나, 애초에 pivot_table을 사용해 집계하면서 펼치는 방법을 고려하세요.

  • 🔁stack ↔ unstack은 같은 수준을 대상으로 하면 상호 복원이 가능
  • 🧱level은 이름 또는 정수로 지정 가능하며, MultiIndex에서 정확히 지정해야 의도한 축 이동이 됨
  • 🧼전개 후 생기는 NaN은 fillnaastype으로 후처리하여 모델/시각화 단계의 오류를 예방

💬 핵심은 ‘어떤 수준을 어느 축으로 이동시킬 것인가’를 명확히 정하는 것입니다.
스키마를 먼저 설계하고, 그에 맞춰 stack/unstack을 적용하면 데이터 정형화가 일관되고 재현성 있게 유지됩니다.

🧱 MultiIndex와 결합하는 데이터 변환 전략

데이터 분석에서는 단순한 행·열 구조보다 여러 계층이 중첩된 MultiIndex를 다루는 경우가 많습니다.
지역·연도·분기처럼 계층적인 인덱스를 활용하면 더 풍부한 구조로 데이터를 표현할 수 있지만, 이때 stackunstack을 통해 축을 자유롭게 이동시키는 전략이 필요합니다.
특히 피벗이나 groupby 결과로 생성된 MultiIndex는 사람이 읽기에는 편리하지 않지만, 변환을 통해 원하는 분석 단위로 깔끔하게 재배치할 수 있습니다.

📊 열 MultiIndex 다루기

여러 지표와 시점을 동시에 표현하면 열이 2단 또는 3단 구조가 됩니다.
이때 stack(level=지표) 또는 stack(level=시점)을 지정하면 특정 축만 내려 행으로 이동시킬 수 있습니다.
이를 통해 시계열 그래프 입력용 long-form 데이터로 쉽게 변환 가능합니다.

CODE BLOCK
import pandas as pd

cols = pd.MultiIndex.from_product([["매출","수익"], ["2023","2024"]], names=["지표","연도"])
df = pd.DataFrame([[100,120,80,90],[150,160,100,110]], index=["서울","부산"], columns=cols)

# 열 MultiIndex를 행으로 전개
stacked = df.stack(level="연도")
print(stacked.head())

📈 행 MultiIndex 다루기

지역, 연도, 분기처럼 여러 수준이 행에 설정되어 있으면 unstack으로 일부 수준을 열로 전개할 수 있습니다.
예를 들어 unstack(“분기”)는 같은 지역·연도 조합의 분기별 값을 옆으로 나란히 보여줍니다.
이렇게 하면 피벗 테이블을 직접 만드는 것보다 유연하고, 다단계 분석에서 필요한 부분만 선택적으로 펼칠 수 있습니다.

  • 📌열 MultiIndex → stack(level=지표/연도)로 행으로 내림
  • 📌행 MultiIndex → unstack(level=분기/연도)로 열로 펼침
  • 📌원하는 수준만 지정해 부분 전개 가능 → 필요 없는 수준은 그대로 유지

💡 TIP: MultiIndex를 다룰 때는 각 수준의 이름을 꼭 지정해 두는 것이 좋습니다.
숫자 인덱스로 level을 지정하면 헷갈리기 쉽고, 유지보수 시 오류를 만들기 쉽습니다.

💬 MultiIndex는 구조만 복잡할 뿐, 올바른 수준을 골라 stack/unstack 하면 원하는 형태로 언제든 변환 가능합니다.
데이터의 유연성을 최대한 살리는 전략이 중요합니다.



⚙️ stack unstack level 파라미터 완벽 가이드

pandas의 stackunstack은 단순히 행과 열을 바꾸는 도구가 아닙니다.
핵심은 level 파라미터를 통해 어떤 인덱스 수준을 이동시킬지를 세밀하게 제어하는 것입니다.
level은 정수(순서 기반) 또는 이름(label 기반)으로 지정할 수 있으며, 여러 개의 수준을 리스트로 동시에 이동시킬 수도 있습니다.
데이터프레임의 인덱스 구조가 복잡할수록 level 지정이 분석 결과에 큰 영향을 주기 때문에 올바른 사용법을 이해하는 것이 중요합니다.

🔢 정수 기반 level 지정

정수로 level을 지정하면 인덱스 계층의 위치를 기준으로 이동합니다.
예를 들어 level=0은 가장 바깥쪽 수준, level=-1은 가장 안쪽 수준을 의미합니다.
정수 방식은 직관적이지만, 인덱스 순서가 바뀌면 코드 유지보수에 어려움이 생길 수 있습니다.

🏷️ 이름 기반 level 지정

MultiIndex를 생성할 때 각 수준에 name을 부여하면 level을 이름으로 지정할 수 있습니다.
예를 들어 level=”연도”처럼 쓰면 순서가 변해도 안정적으로 원하는 수준을 이동할 수 있어 실무에서 권장됩니다.

CODE BLOCK
import pandas as pd

index = pd.MultiIndex.from_product(
    [["서울","부산"], ["2023","2024"]],
    names=["지역","연도"]
)
df = pd.DataFrame({"매출":[100,120,80,90]}, index=index)

# 정수 기반 지정
print(df.unstack(level=1))   # level=1 → "연도"

# 이름 기반 지정
print(df.unstack(level="연도"))

📚 다중 level 지정

stack과 unstack은 리스트 형태로 여러 수준을 동시에 이동할 수 있습니다.
예를 들어 unstack([“연도”,”분기”])를 적용하면 두 수준이 동시에 열로 올라가면서 다차원 테이블이 생성됩니다.
복잡한 분석에서 유용하지만, 메모리 사용량이 급격히 증가할 수 있으므로 주의가 필요합니다.

💎 핵심 포인트:
level은 정수보다는 이름 기반으로 지정하는 것이 안전합니다.
또한 여러 수준을 동시에 이동할 때는 메모리와 연산 속도를 반드시 고려해야 합니다.

⚠️ 주의: 여러 수준을 unstack한 후에는 열 MultiIndex가 깊어져 가독성이 떨어질 수 있습니다.
이럴 때는 swaplevel이나 sort_index로 정리하면 관리가 편해집니다.

🔄 피벗과의 차이 및 실제 활용 패턴

pandas에서 자주 비교되는 도구가 pivot/pivot_tablestack/unstack입니다.
겉보기에는 모두 행과 열을 전환하는 기능을 제공하지만, 의도와 사용 시점에 차이가 있습니다.
피벗은 원본 데이터에서 새로운 구조를 “생성”하는 단계에 가깝고, 스택·언스택은 이미 존재하는 MultiIndex 구조를 “재배치”하는 단계에서 사용합니다.
즉, 피벗은 데이터의 모양을 처음 정의하는 과정이고, 스택·언스택은 만들어진 구조를 다른 뷰(view)로 변환하는 과정입니다.

📌 pivot vs unstack

pivot은 원본 데이터의 열을 지정해 새로운 열 인덱스로 삼습니다.
반면 unstack은 이미 존재하는 행 인덱스의 수준을 열로 올립니다.
따라서 pivot은 “열 생성 중심”이고, unstack은 “인덱스 전개 중심”이라는 차이가 있습니다.
특히 중복된 값이 있을 경우 pivot은 오류를 내지만, pivot_table은 집계를 통해 처리할 수 있습니다.

📌 stack vs melt

stack은 열 MultiIndex를 행으로 이동시키며 long-form 구조를 만드는 방식이고, melt는 기존 열들을 직접 행 값으로 녹여내는 함수입니다.
둘 다 “길게 만들기”지만, melt는 모든 열을 행으로 풀어낼 수 있어 더 범용적입니다.
stack은 MultiIndex가 있을 때 가장 간결한 방법으로 활용됩니다.

CODE BLOCK
import pandas as pd

df = pd.DataFrame({
    "지역":["서울","서울","부산","부산"],
    "분기":["Q1","Q2","Q1","Q2"],
    "매출":[100,120,80,90]
})

# pivot: 새로운 열로 펼치기
wide = df.pivot(index="지역", columns="분기", values="매출")

# unstack: 기존 인덱스를 열로 이동
multi = df.set_index(["지역","분기"])
wide2 = multi.unstack(level="분기")

# melt: 열을 녹여 long-form 생성
long = wide.reset_index().melt(id_vars="지역", var_name="분기", value_name="매출")

구분 pivot/pivot_table stack/unstack
주 목적 새로운 형태 생성 기존 인덱스 재배치
중복값 처리 pivot은 오류 / pivot_table은 집계 중복 불허, 사전 집계 필요
활용 예시 피벗 테이블 생성 뷰 전환 및 계층 정리

💡 TIP: pivot_table로 데이터를 먼저 집계한 뒤, stack/unstack으로 가독성 있는 뷰를 만드는 조합은 실무에서 가장 자주 쓰이는 패턴입니다.



🚀 성능 팁과 오류 대처 체크리스트

데이터 크기가 커지면 stackunstack 과정에서 속도와 메모리 문제가 발생할 수 있습니다.
또한 중복 인덱스나 누락값이 많을 경우 예기치 못한 오류가 발생하기도 합니다.
실무에서는 다음과 같은 점검 포인트를 기억하면 안정적이고 효율적인 전환이 가능합니다.

⚡ 성능 최적화 팁

  • 🔍스택·언스택 후 메모리 사용량이 급격히 늘 수 있으므로 필요한 수준만 선택적 level 지정을 활용
  • 🧹NaN 값이 대량으로 발생하면 sparse=True 옵션을 고려하거나 이후 fillna 처리로 최적화
  • 🗂️자주 쓰는 전환은 피벗 → 스택/언스택 순으로 재사용 가능하게 함수화

🛠️ 오류 대처 전략

실제 사용 시 가장 자주 마주하는 오류는 중복 인덱스와 NaN 처리 문제입니다.
중복된 인덱스를 unstack하려고 하면 ValueError가 발생합니다.
이때는 사전에 groupby 집계로 중복을 제거하거나, pivot_table로 집계하면서 동시에 변환하는 것이 효과적입니다.

CODE BLOCK
import pandas as pd

df = pd.DataFrame({
    "지역":["서울","서울","부산","부산"],
    "분기":["Q1","Q1","Q2","Q2"],
    "매출":[100,120,80,90]
})

# 중복 조합 발생 → unstack 오류 발생
multi = df.set_index(["지역","분기"])

# 해결책 1: groupby 집계
clean = df.groupby(["지역","분기"]).sum().unstack("분기")

# 해결책 2: pivot_table 사용
pivoted = df.pivot_table(index="지역", columns="분기", values="매출", aggfunc="sum")

⚠️ 주의: 데이터가 수백만 행 이상일 경우 스택·언스택을 남용하면 속도가 크게 느려집니다.
반복적 전환이 필요한 경우 처음부터 long-form 데이터로 정규화해 두는 전략이 더 효율적일 수 있습니다.

💬 성능과 오류 모두 “데이터 크기와 인덱스 구조”에 달려 있습니다.
사전에 인덱스를 정리하고 필요한 수준만 전개하는 습관이 효율적인 분석을 만듭니다.

자주 묻는 질문 FAQ

stack과 melt는 어떻게 다른가요?
stack은 열 MultiIndex를 행으로 이동시켜 long-form을 만들고, melt는 지정한 열들을 직접 녹여 행 값으로 변환합니다. melt는 범용적이고, stack은 MultiIndex 상황에서 간단하게 사용할 수 있습니다.
unstack 후 NaN이 생기는 이유는 무엇인가요?
행 인덱스 조합이 존재하지 않으면 해당 위치가 비게 되어 NaN이 채워집니다. 필요하다면 fillna를 사용하거나 집계 시 모든 조합을 채워 넣는 방식으로 해결할 수 있습니다.
stack과 unstack은 역연산 관계인가요?
같은 level을 기준으로 적용하면 서로 역연산 관계에 있습니다. 다만 NaN 처리 여부나 데이터 타입 변환에 따라 완전히 동일한 결과가 아닐 수도 있습니다.
중복된 인덱스가 있으면 왜 unstack이 안 되나요?
동일한 행 인덱스 조합을 열로 펼치면 값이 중복되어 충돌이 생깁니다. 이를 방지하려면 먼저 groupby로 집계하거나 pivot_table을 활용해 집계 후 전개하는 방법을 사용합니다.
stack과 pivot_table을 같이 쓸 수 있나요?
가능합니다. pivot_table로 집계된 결과는 보통 열 MultiIndex 형태를 띠는데, 이때 stack을 적용하면 분석용 long-form 데이터로 쉽게 변환됩니다.
여러 level을 동시에 unstack하면 성능에 문제가 있나요?
두 개 이상 level을 한 번에 unstack하면 열의 경우의 수가 크게 늘어나 메모리 사용량이 증가할 수 있습니다. 데이터 크기에 따라 단계적으로 나누어 처리하는 것이 안전합니다.
stack 사용 시 누락값은 어떻게 처리되나요?
기본적으로 NaN은 결과에서 제외됩니다. 따라서 더 압축된 데이터 구조가 되며, 필요하다면 dropna=False 옵션을 사용해 NaN을 유지할 수도 있습니다.
stack과 unstack은 어떤 경우에 가장 유용한가요?
피벗 결과를 다른 형태로 분석하거나 시각화용 데이터로 변환할 때 가장 유용합니다. MultiIndex 구조를 가볍게 전환해 다양한 뷰를 얻는 데 자주 활용됩니다.

📝 스택 언스택 활용 정리와 데이터 변환 인사이트

pandas의 stackunstack은 데이터 구조를 자유롭게 바꿀 수 있는 강력한 도구입니다.
행과 열을 전환하며 원하는 형태로 데이터를 재구성할 수 있고, 특히 MultiIndex와 결합하면 다양한 분석 뷰를 얻을 수 있습니다.
피벗이 데이터를 새롭게 생성하는 단계라면, 스택과 언스택은 만들어진 구조를 효율적으로 재배치하는 단계라고 할 수 있습니다.
실무에서는 성능 문제나 중복 인덱스로 인한 오류를 예방하기 위해 level 지정, fillna, groupby 집계 같은 기법과 함께 사용하는 것이 좋습니다.
데이터가 복잡해질수록 변환 과정을 반복 가능하게 설계하는 것이 중요하며, stack/unstack은 이를 가능하게 해주는 핵심 함수입니다.


🏷️ 관련 태그 : pandas, python데이터분석, stack, unstack, MultiIndex, 데이터정형화, 데이터변환, 데이터프레임, pivot, 시각화준비