pandas 100제: 판다스 연습문제와 풀이 · 9장
071–080 그룹별 집계
groupby sum·mean, agg, named aggregation, unstack, pivot_table, size, transform, filter, 그룹별 최댓값 행 연습문제
이 장의 목차 13개
카페 매출 데이터로 그룹별 집계를 연습합니다. 준비 코드에 매출 revenue 열이 들어 있습니다.
groupby("store")는 store 값이 같은 행끼리 나누고, 그룹마다 sum 같은 함수를 계산한 다음, 결과를 그룹 키가 인덱스인 표로 합칩니다. 행 수가 그룹 수로 줄어드는 것이 agg/sum이고, 그룹 계산 결과를 원래 행마다 다시 붙여 행 수를 유지하는 것이 transform입니다(078번).
준비 코드와 데이터
노트북 첫 셀에 아래 준비 코드를 붙여 넣어 실행한 뒤 문제를 푸세요.
Pythonimport pandas as pd
sales = pd.DataFrame({
'date': ['2026-03-01', '2026-03-01', '2026-03-02', '2026-03-02',
'2026-03-03', '2026-03-03', '2026-03-04', '2026-03-04'],
'store': ['강남', '홍대', '강남', '홍대', '강남', '홍대', '강남', '홍대'],
'menu': ['아메리카노', '라떼', '라떼', '아메리카노', '아메리카노', '케이크', '케이크', '라떼'],
'qty': [30, 12, 18, 25, 28, 7, 9, 15],
'price': [4500, 5000, 5000, 4500, 4500, 6500, 6500, 5000],
})
sales['revenue'] = sales['qty'] * sales['price']
이 장 실습 노트북 내려받기 (.ipynb) — 준비 코드와 문제가 들어 있고 답 칸은 비어 있습니다. 코랩에서는 파일 → 노트 업로드로 엽니다.
준비 코드를 실행하면 sales는 다음 표가 됩니다(8행 × 6열, 맨 왼쪽 숫자는 인덱스).
결과 date store menu qty price revenue
0 2026-03-01 강남 아메리카노 30 4500 135000
1 2026-03-01 홍대 라떼 12 5000 60000
2 2026-03-02 강남 라떼 18 5000 90000
3 2026-03-02 홍대 아메리카노 25 4500 112500
4 2026-03-03 강남 아메리카노 28 4500 126000
5 2026-03-03 홍대 케이크 7 6500 45500
6 2026-03-04 강남 케이크 9 6500 58500
7 2026-03-04 홍대 라떼 15 5000 75000
장 안의 문제는 번호 순서대로 이어집니다. 앞 문제에서 만든 열이나 변수를 다음 문제가 쓸 수 있으니, 중간부터 풀 때는 준비 코드와 앞의 필요한 문제를 먼저 실행하세요.
071. 그룹 합계
매장별 매출 합계를 구하세요.
힌트 보기
groupby("store")["revenue"].sum().
정답 보기
Pythonsales.groupby('store')['revenue'].sum()
결과store
강남 409500
홍대 293000
Name: revenue, dtype: int64
풀이: 같은 매장의 매출을 모아 더합니다. 합계는 두 매장 합이 전체 매출과 같은지 검산할 수 있습니다.
072. 그룹 평균
메뉴별 평균 판매 수량을 구하세요.
힌트 보기
묶을 열은 menu, 계산할 열은 qty, 함수는 mean().
정답 보기
Pythonsales.groupby('menu')['qty'].mean()
결과menu
라떼 15.000000
아메리카노 27.666667
케이크 8.000000
Name: qty, dtype: float64
풀이: 각 판매 기록의 수량을 평균냅니다. 날짜별 전체 판매량 평균이나 주문 한 건당 평균과는 다릅니다.
073. 여러 집계 한 번에
매장별 수량의 합계·평균·최댓값을 구하세요.
힌트 보기
agg(["sum", "mean", "max"])처럼 함수 이름 리스트를 넘깁니다.
정답 보기
Pythonsales.groupby('store')['qty'].agg(['sum', 'mean', 'max'])
결과 sum mean max
store
강남 85 21.25 30
홍대 59 14.75 25
풀이: 한 열에 여러 집계 함수를 적용해 합계·평균·최댓값 열을 만듭니다.
074. 이름 붙인 집계
메뉴별 총수량(qty 합)과 총매출(revenue 합)을 구하세요.
힌트 보기
agg(새이름=("원본 열", "함수"), ...) — 이름 붙인 집계입니다.
정답 보기
Pythonsales.groupby('menu').agg(총수량=('qty', 'sum'), 총매출=('revenue', 'sum'))
결과 총수량 총매출
menu
라떼 45 225000
아메리카노 83 373500
케이크 16 104000
풀이: 이름 붙인 집계는 결과 열 이름과 원본 열·계산 함수를 함께 지정합니다.
075. 두 기준으로 묶어 펼치기
매장 × 메뉴별 수량 합계를 구하고, 메뉴를 열로 펼치세요.
힌트 보기
groupby(["store", "menu"])로 묶어 합계를 낸 뒤 .unstack()으로 안쪽 인덱스(menu)를 열로 펼칩니다.
정답 보기
Pythonsales.groupby(['store', 'menu'])['qty'].sum().unstack()
결과menu 라떼 아메리카노 케이크
store
강남 18 58 9
홍대 27 25 7
풀이: 두 키로 묶은 결과의 마지막 인덱스 단계(menu)를 unstack으로 열에 옮깁니다.
076. 피벗 테이블
날짜를 행, 매장을 열로 하는 매출 합계 표를 만드세요.
힌트 보기
pivot_table(index=, columns=, values=, aggfunc="sum").
정답 보기
Pythonsales.pivot_table(index='date', columns='store', values='revenue', aggfunc='sum')
결과store 강남 홍대
date
2026-03-01 135000 60000
2026-03-02 90000 112500
2026-03-03 126000 45500
2026-03-04 58500 75000
풀이: aggfunc를 sum으로 명시합니다. 생략하면 기본 평균이어서 질문과 다른 값을 계산합니다.
077. 그룹 크기
메뉴별 판매 기록(행) 수를 구하세요.
힌트 보기
그룹마다 행 수를 세는 함수는 size()입니다.
정답 보기
Pythonsales.groupby('menu').size()
결과menu
라떼 3
아메리카노 3
케이크 2
dtype: int64
풀이: size는 그룹 내 행 수, count는 열별 유효 값 수입니다. 그룹 키 자체의 결측치는 기본 groupby에서 제외됩니다.
size는 빈 값도 셉니다. count는 빈 값을 뺍니다.
078. 그룹 안 비율
각 행의 매출이 해당 매장 전체 매출의 몇 %인지 share 열로 구하세요.
힌트 보기
groupby("store")["revenue"].transform("sum")으로 각 행 옆에 매장 합계를 붙인 뒤 나눕니다.
정답 보기
Pythonstore_total = sales.groupby('store')['revenue'].transform('sum')
sales['share'] = (sales['revenue'] / store_total * 100).round(1)
sales[['store', 'menu', 'revenue', 'share']]
결과 store menu revenue share
0 강남 아메리카노 135000 33.0
1 홍대 라떼 60000 20.5
2 강남 라떼 90000 22.0
3 홍대 아메리카노 112500 38.4
4 강남 아메리카노 126000 30.8
5 홍대 케이크 45500 15.5
6 강남 케이크 58500 14.3
7 홍대 라떼 75000 25.6
풀이: 행 수를 유지하는 transform으로 같은 매장의 총매출을 각 행에 붙인 뒤 비율을 계산합니다. 반올림 합계는 100과 조금 다를 수 있습니다.
079. 조건에 맞는 그룹만
총매출이 20만 원 이상인 메뉴의 행만 남기세요.
힌트 보기
groupby("menu").filter(lambda g: 조건) — 조건은 그룹 g 전체의 매출 합계로 씁니다.
정답 보기
Pythonsales.groupby('menu').filter(lambda g: g['revenue'].sum() >= 200000)
결과 date store menu qty price revenue share
0 2026-03-01 강남 아메리카노 30 4500 135000 33.0
1 2026-03-01 홍대 라떼 12 5000 60000 20.5
2 2026-03-02 강남 라떼 18 5000 90000 22.0
3 2026-03-02 홍대 아메리카노 25 4500 112500 38.4
4 2026-03-03 강남 아메리카노 28 4500 126000 30.8
7 2026-03-04 홍대 라떼 15 5000 75000 25.6
풀이: 개별 행 매출이 아니라 메뉴 그룹 전체 합계로 통과 여부를 정하고 통과한 그룹의 행을 모두 남깁니다.
080. 그룹별 최댓값 행
매장별로 매출이 가장 큰 판매 기록을 구하세요.
힌트 보기
groupby("store")["revenue"].idxmax()가 매장별로 가장 큰 행의 라벨을 줍니다. 그 라벨로 loc.
정답 보기
Pythonsales.loc[sales.groupby('store')['revenue'].idxmax()]
결과 date store menu qty price revenue share
0 2026-03-01 강남 아메리카노 30 4500 135000 33.0
3 2026-03-02 홍대 아메리카노 25 4500 112500 38.4
풀이: 그룹별 최댓값의 행 라벨을 구한 뒤 원본 표에서 행 전체를 선택합니다. 동점은 첫 행이며 인덱스는 유일하다고 가정합니다.
이 장 요약
| 문법 | 하는 일 | 문제 |
|---|---|---|
df.groupby("키")["열"].sum() | 그룹별 합계 | 071–072 |
.agg(["sum", "mean", "max"]) | 여러 집계 한 번에 | 073 |
.agg(새이름=("열", "함수")) | 이름 붙인 집계 | 074 |
groupby([키1, 키2])[열].sum().unstack() | 두 기준 집계를 표로 펼치기 | 075 |
df.pivot_table(index=, columns=, values=, aggfunc=) | 피벗 테이블 | 076 |
groupby("키").size() | 그룹별 행 수 | 077 |
groupby("키")[열].transform("sum") | 그룹 결과를 행마다 붙이기 | 078 |
groupby("키").filter(lambda g: ...) | 조건 맞는 그룹의 행만 | 079 |
df.loc[groupby("키")[열].idxmax()] | 그룹별 최댓값 행 | 080 |
이 장의 점검
그룹 행 수(size)와 유효 값 수(count), 행 수를 줄이는 agg와 유지하는 transform을 구별하세요.
참고: pandas 공식 문서 · 문법이 기억나지 않으면 부록 치트시트를 찾아보세요.