Skip to main content

GroupBy와 집계

GroupBy는 데이터를 특정 기준으로 그룹화한 뒤 각 그룹에 집계 함수를 적용하는 패턴입니다. SQL의 GROUP BY와 동일한 개념이며, 데이터 분석에서 가장 빈번하게 사용되는 연산 중 하나입니다.

학습 목표

  • GroupBy의 Split-Apply-Combine 메커니즘을 이해한다
  • agg로 다양한 집계 함수를 적용할 수 있다
  • transform과 agg의 차이를 이해하고 적절히 사용할 수 있다
  • filter로 그룹 단위 필터링을 수행할 수 있다

왜 중요한가

“부서별 평균 매출”, “고객 등급별 구매 빈도”, “연도별 성장률” 등 비즈니스 분석의 대부분은 그룹별 집계입니다. EDA에서 범주형 변수와 수치형 변수의 관계를 탐색할 때, 피처 엔지니어링에서 그룹 통계를 피처로 만들 때 GroupBy가 핵심입니다.

Split-Apply-Combine

import pandas as pd
import numpy as np

df = pd.DataFrame({
    '부서': ['개발', '영업', '개발', '영업', '개발', '마케팅'],
    '이름': ['김', '이', '박', '최', '정', '강'],
    '연봉': [5000, 4500, 4800, 4200, 5200, 3800],
    '평가': ['A', 'B', 'A', 'A', 'B', 'A']
})

# 기본 groupby
grouped = df.groupby('부서')

# 단일 집계
print(grouped['연봉'].mean())
# 부서
# 개발      5000.0
# 마케팅    3800.0
# 영업      4350.0

# 여러 집계 함수 동시 적용
print(grouped['연봉'].agg(['mean', 'min', 'max', 'count']))

agg — 다중 집계

# 열별 다른 집계 함수
result = df.groupby('부서').agg({
    '연봉': ['mean', 'std'],
    '이름': 'count',
    '평가': lambda x: (x == 'A').sum()  # A 평가 수
})
print(result)

# Named Aggregation (권장 — 가독성 좋음)
result = df.groupby('부서').agg(
    평균연봉=('연봉', 'mean'),
    최대연봉=('연봉', 'max'),
    인원수=('이름', 'count'),
    A등급수=('평가', lambda x: (x == 'A').sum())
)
print(result)

transform — 그룹 통계를 원본에 매핑

transform()agg()와 달리 원본과 같은 shape의 결과를 반환합니다.
# 부서별 평균을 각 행에 매핑
df['부서평균'] = df.groupby('부서')['연봉'].transform('mean')

# 부서 내 연봉 순위
df['부서내순위'] = df.groupby('부서')['연봉'].transform('rank', ascending=False)

# Z-score 정규화 (그룹별)
df['연봉_zscore'] = df.groupby('부서')['연봉'].transform(
    lambda x: (x - x.mean()) / x.std()
)
print(df)
agg()는 그룹당 하나의 값을 반환하여 행이 줄어듭니다. transform()은 각 행에 그룹 통계를 매핑하여 원본 크기를 유지합니다. 피처 엔지니어링에서는 transform()이 더 자주 사용됩니다.

filter — 그룹 단위 필터링

# 인원이 2명 이상인 부서만 남기기
filtered = df.groupby('부서').filter(lambda x: len(x) >= 2)
print(filtered)

# 평균 연봉이 4500 이상인 부서만
high_salary_depts = df.groupby('부서').filter(lambda x: x['연봉'].mean() >= 4500)

다중 그룹화

# 부서 + 평가 등급별 집계
result = df.groupby(['부서', '평가']).agg(
    인원수=('이름', 'count'),
    평균연봉=('연봉', 'mean')
).reset_index()
print(result)

AI/ML에서의 활용

  • 피처 엔지니어링: transform()으로 그룹별 평균, 개수, 비율 등을 피처로 추가합니다
  • 타겟 인코딩: groupby('카테고리')['target'].transform('mean')으로 범주형 피처를 인코딩합니다
  • EDA: 그룹별 통계로 범주형 변수와 타겟 변수의 관계를 탐색합니다
  • 데이터 품질: 그룹별 결측치 비율, 이상치 비율을 계산합니다
agg()는 집계 함수(스칼라 반환)에 최적화되어 있고, apply()는 임의의 함수를 적용할 수 있지만 더 느립니다. 가능하면 agg()를 사용하고, 복잡한 로직이 필요할 때만 apply()를 사용하세요.
groupby('열', as_index=False)로 설정하면 그룹 키가 인덱스가 되지 않고 일반 열로 남습니다. .reset_index()를 호출하는 것과 동일한 효과입니다.

체크리스트

  • GroupBy의 Split-Apply-Combine 패턴을 설명할 수 있다
  • agg로 열별 다른 집계 함수를 적용할 수 있다
  • Named Aggregation 문법을 사용할 수 있다
  • transform과 agg의 차이를 이해하고 상황에 맞게 선택할 수 있다
  • filter로 조건을 만족하는 그룹만 남길 수 있다

다음 문서