14. 멀티인덱싱과 그룹화

🎯 이 장의 목표
  • 그룹화(group-by)로 데이터를 그룹별로 묶어 요약한다
  • split-apply-combine 흐름을 이해한다
  • 멀티인덱싱(여러 층 인덱스)이 무엇인지 안다
💡 팁
"남자/여자별 평균 매출", "요일별 손님 수"처럼 그룹별로 나눠 요약하는 것은 데이터 분석에서 가장 자주 하는 일입니다. 그 핵심 도구가 groupby입니다.

먼저: 그룹화(group-by)가 뭔가요?

그룹화(grouping)같은 값을 가진 행끼리 묶은 뒤, 각 묶음을 요약(평균·합계 등)하는 것입니다. 예를 들어 손님 데이터를 "요일별로 묶어 평균 매출"을 구하는 식이죠.

이 과정은 세 단계로 일어나며, 이를 split-apply-combine이라 부릅니다.

flowchart LR
    A[전체 데이터] -->|Split<br/>그룹으로 쪼갬| B[서울 그룹]
    A -->|Split| C[부산 그룹]
    B -->|Apply<br/>각 그룹 요약| D[서울 평균]
    C -->|Apply| E[부산 평균]
    D -->|Combine<br/>결과 합침| F[도시별 평균 표]
    E -->|Combine| F
    classDef raw fill:#fff3bf,stroke:#f59f00,color:#000
    classDef grp fill:#c3fae8,stroke:#0ca678,color:#000
    classDef res fill:#d3f9d8,stroke:#2f9e44,color:#000
    class A raw
    class B,C,D,E grp
    class F res
🔑 새 용어 — split-apply-combine
그룹화의 세 단계입니다. Split(기준값으로 그룹을 쪼갬) → Apply(각 그룹에 평균 등을 적용) → Combine(결과를 하나의 표로 합침). Pandas의 groupby가 이 셋을 한 번에 해 줍니다.

기본 그룹화

실습 데이터를 만듭니다.

PYTHON
import pandas as pd

df = pd.DataFrame({
    "도시": ["서울", "부산", "서울", "부산", "서울"],
    "성별": ["남", "남", "여", "여", "남"],
    "매출": [100, 150, 200, 120, 180]
})
print(df)

이제 도시별 평균 매출을 구해 봅시다.

PYTHON
print(df.groupby("도시")["매출"].mean())

실행 결과:

CODE
도시
부산    135.0
서울    160.0
Name: 매출, dtype: float64

읽는 법: df.groupby("도시") (도시로 쪼갬) → ["매출"] (매출 열을) → .mean() (평균 내라). 부산 평균은 (150+120)/2=135, 서울은 (100+200+180)/3≈160입니다.

다른 요약도 똑같은 패턴입니다.

PYTHON
print(df.groupby("도시")["매출"].sum())    # 도시별 합계
print(df.groupby("도시")["매출"].count())  # 도시별 개수
print(df.groupby("도시")["매출"].max())    # 도시별 최댓값

실행 결과:

CODE
도시
부산    270
서울    480
Name: 매출, dtype: int64
도시
부산    2
서울    3
Name: 매출, dtype: int64
도시
부산    150
서울    200
Name: 매출, dtype: int64

여러 기준으로 그룹화

기준 열을 리스트로 주면 여러 단계로 묶입니다 (예: 도시 그리고 성별).

PYTHON
print(df.groupby(["도시", "성별"])["매출"].mean())

실행 결과:

CODE
도시  성별
부산  남     150.0
    여     120.0
서울  남     140.0
    여     200.0
Name: 매출, dtype: float64

결과의 왼쪽을 보면 도시성별두 층의 인덱스를 이룹니다. 이것이 다음에 볼 멀티인덱스입니다.

여러 요약을 한 번에 — agg

agg(aggregate, 집계)로 여러 통계를 동시에 구할 수 있습니다.

PYTHON
print(df.groupby("도시")["매출"].agg(["mean", "sum", "count", "max"]))

실행 결과:

CODE
     mean  sum  count  max
도시
부산  135.0  270      2  150
서울  160.0  480      3  200
💡 팁
한눈에 그룹별 여러 지표를 보고 싶을 때 agg가 편리합니다. df.groupby("도시").agg({"매출": "sum", "성별": "count"})처럼 열마다 다른 요약을 줄 수도 있습니다.

멀티인덱싱: 여러 층의 인덱스

🔑 새 용어 — 멀티인덱스(MultiIndex)
인덱스(행 이름표)가 한 층이 아니라 여러 층으로 된 것입니다. 위 groupby(["도시","성별"]) 결과처럼 "도시 안에 성별"이 들어가는 계층 구조를 표현합니다.

멀티인덱스 결과에서 특정 부분을 꺼낼 때는 .loc를 씁니다.

PYTHON
grouped = df.groupby(["도시", "성별"])["매출"].mean()

print(grouped.loc["서울"])          # 서울 전체
print("---")
print(grouped.loc["서울", "여"])     # 서울 + 여

실행 결과:

CODE
성별
남    140.0
여    200.0
Name: 매출, dtype: float64
---
200.0

복잡한 멀티인덱스는 reset_index()평범한 표로 펴는 경우가 많습니다. 다루기가 훨씬 쉬워집니다.

PYTHON
flat = df.groupby(["도시", "성별"])["매출"].mean().reset_index()
print(flat)

실행 결과:

CODE
   도시 성별    매출
0  부산  남  150.0
1  부산  여  120.0
2  서울  남  140.0
3  서울  여  200.0
💡 팁
입문 단계 팁: 멀티인덱스가 헷갈리면, group-by 결과 끝에 .reset_index()를 붙여 평범한 표로 펴서 다루세요. 대부분의 작업이 훨씬 직관적이 됩니다.

value_counts: 가장 간단한 빈도 집계

특정 열의 값별 개수를 셀 때는 value_counts()가 가장 간편합니다(group-by의 단축형).

PYTHON
print(df["도시"].value_counts())

실행 결과:

CODE
도시
서울    3
부산    2
Name: count, dtype: int64

💡 "어떤 값이 몇 번 나오나"를 빠르게 볼 때 애용됩니다. 범주형 데이터(7장 카운트플롯) 분석의 출발점입니다.

🛠 미니 챌린지

PYTHON
import pandas as pd
df = pd.DataFrame({
    "부서": ["영업", "개발", "영업", "개발", "영업"],
    "직급": ["사원", "사원", "대리", "대리", "사원"],
    "연봉": [3000, 3500, 4000, 4200, 3200]
})
  1. 부서별 평균 연봉을 구하세요.
  2. 부서별·직급별 평균 연봉을 구하세요.
  3. 부서별로 연봉의 합계·평균·인원수를 agg로 한 번에 구하세요.

✅ 미니 챌린지 해설

PYTHON
# 1. 부서별 평균
print(df.groupby("부서")["연봉"].mean())

실행 결과:

CODE
부서
개발    3850.0
영업    3400.0
Name: 연봉, dtype: float64
PYTHON
# 2. 부서별·직급별 평균
print(df.groupby(["부서", "직급"])["연봉"].mean())

실행 결과:

CODE
부서  직급
개발  대리    4200.0
    사원    3500.0
영업  대리    4000.0
    사원    3133.333333
Name: 연봉, dtype: float64
PYTHON
# 3. agg로 한 번에
print(df.groupby("부서")["연봉"].agg(["sum", "mean", "count"]))

실행 결과:

CODE
       sum    mean  count
부서
개발   7700  3850.0      2
영업  10200  3400.0      3

이 장에서 배운 것

  • 그룹화는 split-apply-combine(쪼개기-적용-합치기)으로 그룹별 요약을 만든다.
  • df.groupby("열")["대상"].mean()이 기본 패턴이며, sum·count·max 등도 같다.
  • 여러 기준(groupby(["A","B"]))은 멀티인덱스 결과를 낳는다.
  • agg로 여러 통계를 한 번에, value_counts로 값별 빈도를 빠르게 센다.
  • 멀티인덱스가 복잡하면 reset_index()로 평범한 표로 펴서 다룬다.

✍️ 확인 문제

  1. split-apply-combine의 세 단계는 각각 무엇을 하나요?
  2. df.groupby("도시")["매출"].sum()은 무엇을 구하나요?
  3. 한 열의 값별 개수를 가장 간단히 세는 메서드는 무엇인가요?
데이터를 합치고 요약하는 법까지 익혔습니다! 이제 결과를 눈으로 보는 시각화로 넘어갑니다. 먼저 가장 기본인 Matplotlib입니다.
👉 15. Matplotlib 기초 그래프