본문으로 건너뛰기

pandas 100제: 판다스 연습문제와 풀이 · 9장

071–080 그룹별 집계

groupby sum·mean, agg, named aggregation, unstack, pivot_table, size, transform, filter, 그룹별 최댓값 행 연습문제

9분 읽기

이 장의 목차 13개

카페 매출 데이터로 그룹별 집계를 연습합니다. 준비 코드에 매출 revenue 열이 들어 있습니다.

매장(store)별로 행을 나누고 매출을 더해 합치는 groupby 과정
매장(store)별로 행을 나누고 매출을 더해 합치는 groupby 과정

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 공식 문서 · 문법이 기억나지 않으면 부록 치트시트를 찾아보세요.

전체 메뉴