포스트

[Python 100일 챌린지] Day 66 - Pandas 그룹화와 집계

[Python 100일 챌린지] Day 66 - Pandas 그룹화와 집계

엑셀의 피벗 테이블을 Python으로! 📊

데이터를 그룹별로 나누고, 합계/평균을 구하고… groupby() 하나면 복잡한 분석도 쉽게! 실무에서 정말 많이 쓰이는 기능이에요! 💪

(30-40분 완독 ⭐⭐)

🎯 오늘의 학습 목표

📚 사전 지식


🎯 학습 목표 1: groupby() 기초 익히기

1.1 그룹화란?

💡 그룹화(Grouping)를 비유하면?

학교 반별 평균 점수 구하기를 생각해보세요! 📚

  1. 학생들을 반별로 묶고 (그룹화)
  2. 각 반의 평균 점수를 계산 (집계)
1
2
3
4
5
6
전체 학생 데이터
┌─────────────────┐
│ 1반: 80, 85, 90 │ → 평균 85
│ 2반: 70, 75, 80 │ → 평균 75
│ 3반: 90, 95, 85 │ → 평균 90
└─────────────────┘

이 과정이 바로 groupby() → 집계함수예요!

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd

# 샘플 데이터
df = pd.DataFrame({
    '부서': ['영업', '개발', '영업', '개발', '영업', '개발'],
    '이름': ['철수', '영희', '민수', '지영', '현수', '수진'],
    '월급': [300, 400, 350, 450, 320, 420],
    '연차': [3, 5, 2, 7, 4, 3]
})

print(df)

출력:

1
2
3
4
5
6
7
   부서  이름   월급  연차
0  영업  철수  300    3
1  개발  영희  400    5
2  영업  민수  350    2
3  개발  지영  450    7
4  영업  현수  320    4
5  개발  수진  420    3

1.2 기본 groupby()

💡 groupby() 동작 순서 (Split-Apply-Combine)

단계 영어 설명 예시
1단계 Split 그룹별로 나누기 부서별로 데이터 분리
2단계 Apply 각 그룹에 함수 적용 각 부서별 평균 계산
3단계 Combine 결과 합치기 하나의 결과로 합침
1
2
3
4
5
# 부서별 그룹화
grouped = df.groupby('부서')

# 부서별 평균 월급
print(grouped['월급'].mean())

출력:

1
2
3
4
부서
개발    423.333333
영업    323.333333
Name: 월급, dtype: float64

1.3 여러 집계 한 번에

1
2
3
4
# 부서별 모든 숫자 컬럼 평균
print(df.groupby('부서')[['월급', '연차']].mean())
# 또는 Pandas 2.0+에서는:
# print(df.groupby('부서').mean(numeric_only=True))

출력:

1
2
3
4
        월급      연차
부서
개발  423.33  5.00
영업  323.33  3.00

💡 Pandas 2.0+ 참고: 숫자가 아닌 열이 포함된 경우 mean(numeric_only=True)를 사용하거나, 숫자 열만 명시적으로 선택하세요!


🎯 학습 목표 2: 다양한 집계 함수 사용하기

2.1 기본 집계 함수들

💡 집계(Aggregation)란?

여러 값을 하나의 값으로 요약하는 것이에요!

집계 함수 의미 예시 (10, 20, 30)
sum() 합계 60
mean() 평균 20
max() 최대값 30
min() 최소값 10
count() 개수 3
std() 표준편차 10
median() 중앙값 20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd

df = pd.DataFrame({
    '부서': ['영업', '개발', '영업', '개발', '영업'],
    '월급': [300, 400, 350, 450, 320]
})

grouped = df.groupby('부서')['월급']

print("합계:", grouped.sum().to_dict())
print("평균:", grouped.mean().to_dict())
print("최대:", grouped.max().to_dict())
print("최소:", grouped.min().to_dict())
print("개수:", grouped.count().to_dict())

2.2 agg()로 여러 함수 적용

💡 agg는 aggregate(집계하다)의 줄임말이에요!

여러 집계 함수를 한 번에 적용할 수 있어서 매우 편리해요.

1
2
3
4
5
6
7
8
9
10
import pandas as pd

df = pd.DataFrame({
    '부서': ['영업', '개발', '영업', '개발', '영업'],
    '월급': [300, 400, 350, 450, 320]
})

# 여러 집계 함수 동시에
result = df.groupby('부서')['월급'].agg(['sum', 'mean', 'max', 'min'])
print(result)

출력:

1
2
3
4
       sum    mean  max  min
부서
개발   850  425.0  450  400
영업   970  323.3  350  300

2.3 열마다 다른 집계

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd

df = pd.DataFrame({
    '부서': ['영업', '개발', '영업', '개발', '영업'],
    '월급': [300, 400, 350, 450, 320],
    '연차': [3, 5, 2, 7, 4]
})

# 열마다 다른 함수 적용
result = df.groupby('부서').agg({
    '월급': 'mean',  # 평균
    '연차': 'max'    # 최대값
})
print(result)

출력:

1
2
3
4
          월급  연차
부서
개발  425.0    7
영업  323.3    4

🎯 학습 목표 3: 다중 그룹화와 피벗 테이블

3.1 다중 컬럼 그룹화

💡 다중 그룹화란?

하나의 기준이 아니라 여러 기준으로 동시에 그룹을 나누는 거예요!

예: “부서별 평균”이 아니라 “부서+직급별 평균”을 구하고 싶을 때

1
2
부서별만: 개발팀 평균, 영업팀 평균
부서+직급별: 개발팀-사원 평균, 개발팀-대리 평균, 영업팀-사원 평균...
1
2
3
4
5
6
7
8
9
10
11
import pandas as pd

df = pd.DataFrame({
    '부서': ['영업', '영업', '개발', '개발', '영업', '개발'],
    '직급': ['사원', '대리', '사원', '대리', '사원', '대리'],
    '월급': [300, 400, 350, 500, 320, 480]
})

# 부서 + 직급별 그룹화
result = df.groupby(['부서', '직급'])['월급'].mean()
print(result)

출력:

1
2
3
4
5
6
부서  직급
개발  대리    490.0
      사원    350.0
영업  대리    400.0
      사원    310.0
Name: 월급, dtype: float64

3.2 피벗 테이블

💡 피벗 테이블이란?

엑셀의 피벗 테이블과 완전히 같은 개념이에요!

피벗 테이블 구성요소 역할 예시
values 계산할 값 월급, 판매량
index 행으로 표시 부서
columns 열로 표시 직급
aggfunc 집계 함수 mean, sum

비유: 엑셀에서 드래그&드롭으로 피벗 테이블 만들듯이, Pandas에서는 파라미터로 지정해요!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd

df = pd.DataFrame({
    '부서': ['영업', '영업', '개발', '개발', '영업', '개발'],
    '직급': ['사원', '대리', '사원', '대리', '사원', '대리'],
    '월급': [300, 400, 350, 500, 320, 480]
})

# 엑셀 피벗 테이블처럼!
pivot = pd.pivot_table(df,
    values='월급',        # 값
    index='부서',         # 행
    columns='직급',       # 열
    aggfunc='mean'       # 집계 함수
)
print(pivot)

출력:

1
2
3
4
직급   대리     사원
부서
개발  490.0  350.0
영업  400.0  310.0

3.3 피벗 테이블 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pandas as pd

df = pd.DataFrame({
    '부서': ['영업', '영업', '개발', '개발', '영업', '개발'],
    '직급': ['사원', '대리', '사원', '대리', '사원', '대리'],
    '월급': [300, 400, 350, 500, 320, 480],
    '성과': [85, 90, 88, 95, 82, 92]
})

# 여러 값, 여러 집계
pivot = pd.pivot_table(df,
    values=['월급', '성과'],
    index='부서',
    columns='직급',
    aggfunc={'월급': 'mean', '성과': 'max'}
)
print(pivot)

🎯 학습 목표 4: 실전 데이터 분석 예제

4.1 판매 데이터 분석

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import pandas as pd

# 판매 데이터
sales = pd.DataFrame({
    '날짜': ['2024-01', '2024-01', '2024-02', '2024-02', '2024-01', '2024-02'],
    '제품': ['A', 'B', 'A', 'B', 'A', 'B'],
    '지역': ['서울', '서울', '서울', '부산', '부산', '부산'],
    '판매량': [100, 150, 120, 80, 90, 200],
    '매출': [1000, 2250, 1200, 1200, 900, 3000]
})

# 1. 월별 총 매출
monthly = sales.groupby('날짜')['매출'].sum()
print("=== 월별 매출 ===")
print(monthly)

# 2. 제품별 평균 판매량
product = sales.groupby('제품')['판매량'].mean()
print("\n=== 제품별 평균 판매량 ===")
print(product)

# 3. 지역-제품별 매출
pivot = pd.pivot_table(sales,
    values='매출',
    index='지역',
    columns='제품',
    aggfunc='sum'
)
print("\n=== 지역-제품별 매출 ===")
print(pivot)

4.2 성적 데이터 분석

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd

# 학생 성적
scores = pd.DataFrame({
    '학년': [1, 1, 1, 2, 2, 2, 3, 3, 3],
    '': ['A', 'A', 'B', 'A', 'B', 'B', 'A', 'A', 'B'],
    '이름': ['철수', '영희', '민수', '지영', '현수', '수진', '동훈', '미나', '준호'],
    '국어': [85, 90, 78, 92, 88, 76, 95, 89, 82],
    '수학': [90, 85, 82, 88, 75, 80, 92, 87, 78]
})

# 학년별 평균
print("=== 학년별 평균 ===")
print(scores.groupby('학년')[['국어', '수학']].mean())

# 학년-반별 국어 평균
print("\n=== 학년-반별 국어 평균 ===")
print(scores.groupby(['학년', ''])['국어'].mean())

💡 실전 팁

✅ 자주 쓰는 패턴

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd

df = pd.DataFrame({
    '그룹': ['A', 'A', 'B', 'B', 'A'],
    '이름': ['철수', '영희', '민수', '지영', '현수'],
    '': [10, 20, 30, 40, 15]
})

# 그룹별 상위 N개
print(df.groupby('그룹').head(2))

# 그룹별 정렬
print(df.sort_values(['그룹', ''], ascending=[True, False]))

# 그룹 크기
print(df.groupby('그룹').size())

✅ 그룹별 최대/최소 값을 가진 행 찾기 (idxmax/idxmin)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd

df = pd.DataFrame({
    '부서': ['영업', '영업', '개발', '개발'],
    '이름': ['철수', '영희', '민수', '지영'],
    '급여': [300, 400, 350, 500]
})

# 부서별 최고 급여자의 인덱스 찾기
idx = df.groupby('부서')['급여'].idxmax()
print(idx)
# 부서
# 개발    3
# 영업    1

# 해당 인덱스로 전체 행 가져오기
print(df.loc[idx])
#    부서  이름   급여
# 3  개발  지영  500
# 1  영업  영희  400

💡 idxmax()는 인덱스를 반환해요. 이 인덱스를 df.loc[]에 넣으면 해당 행 전체를 가져올 수 있어요!


🧪 연습 문제

문제: 직원 데이터 분석

주어진 데이터에서 1) 부서별 평균 급여, 2) 부서-직급별 인원 수, 3) 부서별 최고 급여자 찾기

1
2
3
4
5
6
employees = pd.DataFrame({
    '이름': ['김철수', '이영희', '박민수', '최지영', '정현수', '강수진'],
    '부서': ['영업', '개발', '영업', '개발', '영업', '개발'],
    '직급': ['사원', '대리', '대리', '과장', '사원', '사원'],
    '급여': [3000, 4500, 4000, 5500, 3200, 4000]
})
💡 힌트 보기

단계별 접근법:

  1. 부서별 평균 급여
    • groupby('부서')['급여'].mean() 사용
  2. 부서-직급별 인원 수
    • 다중 그룹화: groupby(['부서', '직급'])
    • 개수 세기: .size() (행 개수) 또는 .count() (값 개수)
  3. 부서별 최고 급여자 찾기
    • idxmax()인덱스 찾기: groupby('부서')['급여'].idxmax()
    • df.loc[인덱스]해당 행 가져오기

핵심 코드 구조:

1
2
3
4
5
6
7
8
9
# 평균
df.groupby('그룹열')['값열'].mean()

# 다중 그룹화 + 개수
df.groupby(['그룹열1', '그룹열2']).size()

# 최대값 가진 행 찾기
idx = df.groupby('그룹열')['값열'].idxmax()
df.loc[idx]
정답 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd

employees = pd.DataFrame({
    '이름': ['김철수', '이영희', '박민수', '최지영', '정현수', '강수진'],
    '부서': ['영업', '개발', '영업', '개발', '영업', '개발'],
    '직급': ['사원', '대리', '대리', '과장', '사원', '사원'],
    '급여': [3000, 4500, 4000, 5500, 3200, 4000]
})

# 1. 부서별 평균 급여
print("=== 부서별 평균 급여 ===")
print(employees.groupby('부서')['급여'].mean())

# 2. 부서-직급별 인원 수
print("\n=== 부서-직급별 인원 수 ===")
print(employees.groupby(['부서', '직급']).size())

# 3. 부서별 최고 급여자
print("\n=== 부서별 최고 급여자 ===")
idx = employees.groupby('부서')['급여'].idxmax()
print(employees.loc[idx])

📝 오늘 배운 내용 정리

  1. groupby(): df.groupby('컬럼') - 그룹별 분석
  2. 집계 함수: sum(), mean(), max(), min(), count()
  3. agg(): 여러 집계 함수 동시 적용
  4. pivot_table(): 엑셀 피벗 테이블처럼 분석

🔗 관련 자료


📚 이전 학습

Day 65: Pandas 데이터 조작 ⭐⭐

어제는 필터링, 정렬, 결측값 처리를 배웠어요!

📚 다음 학습

Day 67: Matplotlib 기초 ⭐⭐


“늦었다고 생각할 때가 가장 빠른 때입니다. 오늘도 한 걸음 성장했어요!” 🚀

Day 66/100 Phase 7: 데이터 분석 기초 #100DaysOfPython
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.