[Python 100일 챌린지] Day 66 - Pandas 그룹화와 집계
엑셀의 피벗 테이블을 Python으로! 📊
데이터를 그룹별로 나누고, 합계/평균을 구하고… groupby() 하나면 복잡한 분석도 쉽게! 실무에서 정말 많이 쓰이는 기능이에요! 💪
(30-40분 완독 ⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
- Day 63: Pandas 기초 - DataFrame 기본
- Day 65: Pandas 데이터 조작 - 필터링, 정렬
🎯 학습 목표 1: groupby() 기초 익히기
1.1 그룹화란?
💡 그룹화(Grouping)를 비유하면?
학교 반별 평균 점수 구하기를 생각해보세요! 📚
- 학생들을 반별로 묶고 (그룹화)
- 각 반의 평균 점수를 계산 (집계)
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]
})
💡 힌트 보기
단계별 접근법:
- 부서별 평균 급여
groupby('부서')['급여'].mean()사용
- 부서-직급별 인원 수
- 다중 그룹화:
groupby(['부서', '직급']) - 개수 세기:
.size()(행 개수) 또는.count()(값 개수)
- 다중 그룹화:
- 부서별 최고 급여자 찾기
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])
📝 오늘 배운 내용 정리
- groupby():
df.groupby('컬럼')- 그룹별 분석 - 집계 함수:
sum(),mean(),max(),min(),count() - agg(): 여러 집계 함수 동시 적용
- pivot_table(): 엑셀 피벗 테이블처럼 분석
🔗 관련 자료
📚 이전 학습
Day 65: Pandas 데이터 조작 ⭐⭐
어제는 필터링, 정렬, 결측값 처리를 배웠어요!
📚 다음 학습
Day 67: Matplotlib 기초 ⭐⭐
“늦었다고 생각할 때가 가장 빠른 때입니다. 오늘도 한 걸음 성장했어요!” 🚀
Day 66/100 Phase 7: 데이터 분석 기초 #100DaysOfPython
