Skip to content

Pandas GroupBy: Aggregation·Transform·Apply 완벽 가이드

Updated on

split-apply-combine 패턴은 Pandas의 핵심입니다. 하지만 agg, transform, apply 선택이나 정렬, 누락된 그룹, SettingWithCopy 때문에 자주 막힙니다.

PAS 관점:

  • 문제: 그룹 계산이 불투명하고 느리게 느껴지며, 필요한 것은 합계나 행 단위 비율뿐인 경우가 많다.
  • 심화: apply 남용이나 as_index=False 누락은 예상치 못한 형태, 중복 컬럼, 느린 코드로 이어진다.
  • 해결: agg로 요약, transform으로 행 단위 피처, apply는 꼭 필요할 때만. 정렬과 누락 그룹 옵션을 명시적으로 설정한다.

언제 어떤 메서드를 쓸까 (빠른 표)

메서드반환 형태적합한 용도예시
agg / aggregate그룹당 1행요약, 여러 메트릭df.groupby("team").agg(avg_score=("score","mean"))
transform입력과 동일한 길이그룹 기반 행 피처df["z"] = df.groupby("team")["score"].transform("zscore")
apply유연agg/transform으로 안 되는 복잡 처리df.groupby("team").apply(custom_fn)

예제 데이터

import pandas as pd
 
data = {
    "team": ["A", "A", "B", "B", "B", "C"],
    "player": ["x1", "x2", "y1", "y2", "y3", "z1"],
    "score": [9, 7, 8, 6, 10, 3],
    "minutes": [30, 25, 28, 32, 20, 15],
}
df = pd.DataFrame(data)

집계 패턴 (agg)

summary = (
    df.groupby("team", as_index=False)
      .agg(
          avg_score=("score", "mean"),
          max_score=("score", "max"),
          minutes_played=("minutes", "sum"),
      )
)
  • Named aggregation으로 컬럼명을 깔끔하게 유지.
  • as_index=Falseteam을 컬럼으로 유지(머지/시각화 용이).
  • sort=False로 입력 순서를 보존.

한 컬럼에 여러 집계

df.groupby("team", as_index=False).agg(
    score_mean=("score", "mean"),
    score_std=("score", "std"),
    score_count=("score", "size"),
)

행 단위 피처 (transform)

transform은 행 수를 유지하며 그룹 메트릭을 되돌려준다.

df["score_pct_of_team"] = (
    df["score"] / df.groupby("team")["score"].transform("sum")
)

팀별 Z-스코어:

df["score_z"] = (
    df.groupby("team")["score"]
      .transform(lambda s: (s - s.mean()) / s.std(ddof=0))
)

transform 활용 사례:

  • 그룹 합계 대비 비율
  • 정규화 점수
  • 그룹 플래그(rank, cumcount 등)

apply가 필요한 경우

apply는 유연하지만 느리다. agg/transform이 불가할 때만.

def top_n(group, n=2):
    return group.nlargest(n, "score")
 
top_players = df.groupby("team").apply(top_n, n=1).reset_index(drop=True)

apply를 쓸 때:

  • 그룹마다 반환 행 수가 달라야 할 때
  • 벡터화할 수 없는 파이썬 로직이 필요할 때

누락 그룹과 정렬 제어

result = (
    df.groupby("team", dropna=False, sort=False)
      .agg(avg_score=("score", "mean"))
)
  • dropna=FalseNaN 그룹 라벨을 유지.
  • sort=False로 재정렬을 방지(순서에 의미가 있을 때).

여러 키와 인덱스에서 GroupBy

multi = (
    df.groupby(["team", "player"], as_index=False)
      .agg(score_mean=("score", "mean"))
)

인덱스 레벨로 그룹화:

df2 = df.set_index(["team", "player"])
df2.groupby(level="team")["score"].mean()

흔한 함정

문제해결
예상치 못한 형태 (Series vs DataFrame)as_index=False 설정 또는 이후 reset_index()
머지 후 컬럼명 충돌Named aggregation으로 컬럼명 지정
apply로 느린 속도agg/transform 또는 벡터화 메서드로 대체
출력에서 카테고리가 사라짐observed=False 유지, dropna=FalseNaN 보존

내보내기·시각화 팁

  • 집계 후 정렬: summary.sort_values("avg_score", ascending=False).
  • 차트용 피벗: summary.pivot(index="team", values="avg_score").

관련 가이드


핵심 정리

  • 요약은 agg, 행 피처는 transform, apply는 최소화.
  • as_indexreset_index로 형태를 제어.
  • sort, dropna로 순서와 누락 라벨을 관리.
  • 벡터화 메서드를 우선해 파이프라인을 빠르게 유지.