groupby는 split(키로 쪼갬) → apply(각 조각에 함수) → combine(결과 합침) 3단계입니다.
조각에 적용하는 함수는 성격에 따라 4가지로 갈립니다: agg(그룹당 스칼라), transform(원본과 같은 shape), filter(그룹 통째로 포함/제외), apply(가장 유연, 가장 느림).
df.groupby(...)는 즉시 계산하지 않는 지연 객체이며, 뒤에 오는 연산에서 실제로 평가됩니다.
import pandas as pd
import numpy as npdf.groupby("city")["amount"].sum()
# city
# Busan 12000
# Seoul 45000city 값이 같은 행끼리 묶음.amount에 sum.gb = df.groupby("city") # DataFrameGroupBy - 아직 계산 안 함(지연)
gb.groups # {'Seoul': [행 인덱스...], ...}
gb.get_group("Seoul") # 특정 그룹만 DataFrame으로
for key, sub in gb: # 그룹 순회 (디버깅용, 실무는 지양)
...agg — 그룹당 요약값df.groupby("city")["amount"].agg(["sum", "mean", "count"])
df.groupby("city").agg({"amount": "sum", "qty": "mean"}) # 열별 다른 함수
# named aggregation (권장) — 결과 열 이름을 명시
df.groupby("city").agg(
total_amount=("amount", "sum"),
avg_qty=("qty", "mean"),
n_orders=("amount", "count"),
)
df.groupby("city")["amount"].agg(lambda s: s.max() - s.min()) # 커스텀💡 이럴 때: 대시보드용 요약표(그룹별 합계·평균·건수). named aggregation을 쓰면 결과 열 이름이 깔끔해 이후 처리가 쉽습니다.
💡 이럴 때: 다중 키 집계 — groupby(["city", "category"]). 결과는 MultiIndex이며, reset_index()로 평평한 표로 되돌립니다.
transform — 원본과 같은 길이로 되돌림# 그룹 평균을 각 행에 브로드캐스트
df["city_avg"] = df.groupby("city")["amount"].transform("mean")
# 그룹 내 표준화 (z-score)
df["amount_z"] = df.groupby("city")["amount"].transform(lambda s: (s - s.mean()) / s.std())
# 그룹 평균으로 결측 대체
df["amount"] = df["amount"].fillna(df.groupby("city")["amount"].transform("mean"))agg는 그룹당 1개 값으로 줄이지만, transform은 그룹 통계를 원래 행 위치에 다시 펼칩니다. 파생 열 만들 때 필수.
💡 이럴 때: "각 매장 평균 대비 이 주문이 얼마나 큰가", "그룹 내 비중(
x / group_sum)" 같은 상대 지표 열을 만들 때.
filter — 그룹 단위 선택# 주문이 10건 이상인 도시의 행만 남김
df.groupby("city").filter(lambda g: len(g) >= 10)
# 그룹 평균 금액이 임계값 넘는 그룹만
df.groupby("city").filter(lambda g: g["amount"].mean() > 1000)조건을 만족하는 그룹의 모든 행을 통째로 유지/제거합니다.
apply — 가장 유연, 가장 느림# 그룹별 상위 3개 행
df.groupby("city", group_keys=False).apply(lambda g: g.nlargest(3, "amount"))
# 그룹별 여러 산출물을 한 번에
def summary(g):
return pd.Series({
"n": len(g),
"revenue": g["amount"].sum(),
"top_customer": g.loc[g["amount"].idxmax(), "user_id"],
})
df.groupby("city").apply(summary)apply는 그룹을 통째로 받아 무엇이든 반환할 수 있지만, 파이썬 함수 호출이 그룹 수만큼 일어나 느립니다. agg/transform으로 표현 가능하면 그쪽을 씁니다.
df.groupby(["city", "grade"], observed=True, as_index=False)["amount"].sum()💡 이럴 때:
category열로 groupby 하는데 결과 행이 이상하게 많으면observed=True를 빠뜨린 것입니다 (없는 조합까지 0으로 생성).
df.groupby("city")["amount"].describe()
df.groupby("city").size() # 그룹별 행 수 (결측 포함)
df.groupby("city")["user_id"].nunique()
df.groupby("city")["amount"].cumsum() # 그룹 내 누적합
df.pivot_table(index="city", columns="grade", values="amount", aggfunc="sum")pivot_table은 groupby + unstack의 조합입니다
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.