[판다스 17화] groupby와 agg 함수로 그룹별 집계 활용법 정리 | Python Pandas | 빅분기, 빅데이터분석기사 실기

파이썬(Python) 판다스(Pandas) 라이브러리를 활용한 데이터 분석 작업에서 전체 데이터를 특정 기준에 따라 그룹으로 나누어 요약 통계량을 계산하는 작업은 빈번하게 발생합니다. 예를 들어 기업의 매출 데이터에서 “부서별 평균 급여”, “지역별 총매출액”, 또는 “상품 카테고리별 최고 및 최저 가격”을 도출하는 일련의 과정입니다.

이러한 요약 작업을 판다스에서는 groupby()agg() 함수를 통해 제어합니다. 이는 데이터 분석 방법론 중 하나인 ‘분할-적용-결합(Split-Apply-Combine)’ 전략을 기반으로 작동하며, 현업 실무 데이터 가공은 물론 빅데이터분석기사(빅분기) 실기 시험의 작업형 필수 문항으로 출제됩니다. 이번 글에서는 두 함수의 핵심 사용법과 비즈니스 가상 데이터를 활용한 다중 집계 기법을 체계적으로 살펴보겠습니다.

1. 그룹 분석의 기초: groupby() 함수의 작동 원리

groupby() 함수는 동일한 값을 가진 데이터들을 하나의 그룹으로 묶어주는 역할을 합니다. SQL의 GROUP BY 문법이나 엑셀의 피벗 테이블 기능과 유사한 성격을 가집니다.

1.1. 단일 컬럼 기준의 그룹 집계

가상의 프랜차이즈 매장 매출 데이터를 바탕으로 특정 지점별 매출액 평균을 구하는 시나리오를 살펴보겠습니다.

import pandas as pd

# 가상 매장 매출 데이터 생성
store_df = pd.DataFrame({
    '지점명': ['강남점', '강남점', '종로점', '홍대점', '종로점', '홍대점'],
    '상품군': ['음료', '베이커리', '음료', '베이커리', '베이커리', '음료'],
    '판매량': [120, 45, 80, 60, 55, 110],
    '매출액': [600000, 225000, 400000, 360000, 275000, 550000]
})

# 지점별 매출액 평균 계산
branch_sales = store_df.groupby('지점명')['매출액'].mean()
print(branch_sales)

groupby('지점명')을 지정하면 판다스는 내부적으로 ‘강남점’, ‘종로점’, ‘홍대점’이라는 고유의 그룹으로 테이블을 분할(Split)합니다. 이후 대괄호로 선택한 수치형 컬럼(['매출액'])에 평균 함수(.mean())를 적용(Apply)하여 그룹별 연산 결과를 하나의 시리즈 구조로 결합(Combine)하여 반환합니다.

1.2. 다중 컬럼 기준의 복합 그룹 지정

둘 이상의 기준 열을 사용하여 데이터를 더 촘촘하게 세분화하고 싶다면, groupby() 내부에 컬럼 명칭을 리스트 형태로 전달하면 됩니다.

# 지점명과 상품군을 동시에 기준으로 삼아 판매량의 합계 계산
multi_group = store_df.groupby(['지점명', '상품군'])['판매량'].sum()

이 연산의 결과는 여러 개의 인덱스가 계층을 이루는 멀티인덱스(MultiIndex) 구조를 가집니다. 이를 일반적인 평면 테이블 형태로 변환하여 다음 전처리에 연동하고 싶다면 뒤에 .reset_index()를 연결하여 인덱스를 열로 재정돈하는 방식을 권장합니다.

2. 정밀한 대량 연산을 위한 agg() 함수 활용법

groupby() 뒤에 .mean()이나 .sum()을 붙이는 방식은 전체 그룹에 대해 한 가지 통계량만 구할 수 있다는 한계가 있습니다. 만약 “판매량은 총합을 구하고, 매출액은 평균과 최댓값을 동시에 구하고 싶다”처럼 열마다 서로 다른 집계 함수를 매핑해야 할 때는 agg() (Aggregate) 함수를 활용해야 합니다.

2.1. 딕셔너리를 활용한 열별 차등 집계

agg() 함수 내부에 딕셔너리 형태로 {'컬럼명': '적용할함수'} 구조를 정의하면, 각 피처 변수의 성격에 맞는 통계 연산을 한 번에 제어할 수 있어 가독성이 향상됩니다.

# 컬럼별로 각기 다른 집계 함수 적용
aggregated_df = store_df.groupby('지점명').agg({
    '판매량': 'sum',
    '매출액': 'mean'
})

2.2. 하나의 컬럼에 다중 통계량 동시 매핑

특정 열에 대해 여러 통계 지표를 평행하게 출력하고 싶다면 집계 함수를 리스트로 묶어서 전달합니다.

# 매출액에 대해 평균, 최댓값, 최솟값을 동시에 계산
multi_stats = store_df.groupby('지점명').agg({
    '매출액': ['mean', 'max', 'min']
})

이 연산을 수행하면 컬럼 헤더가 MultiIndex 형태로 생성됩니다. 실무 가공 시 가독성을 높이기 위해 상위 단과 하위 단 컬럼명을 매출액_mean, 매출액_max 형태의 단일 컬럼명으로 변경하고 싶다면 아래와 같이 네이밍 규칙에 따라 컬럼명을 평탄화하여 사용하는 편이 효율적입니다.

# 2단 컬럼 구조를 1단으로 병합 정돈하는 실무 패턴
multi_stats.columns = [f"{col[0]}_{col[1]}" for col in multi_stats.columns]
multi_stats = multi_stats.reset_index()

3. 그룹 집계 데이터 처리 시 주요 예외 처리 및 유의사항

3.1. 수치형 데이터가 아닌 열 자동 제외 현상

판다스 버전에 따라 groupby().mean()과 같은 집계 함수의 numeric_only 처리 방식이 달라질 수 있습니다. 따라서 의도하지 않은 오류를 방지하려면 집계 대상이 되는 수치형 컬럼을 명시적으로 선택하여 연산하는 습관을 갖는 것이 안전합니다.

따라서 그룹 연산을 수행하기 전에 계산 대상이 되는 수치형 변수 컬럼만 명시적으로 슬라이싱하여 groupby 구조에 전달하거나, 수치 연산이 가능한 컬럼만 선별해 두는 작업이 안전합니다.

3.2. 결측치(NaN)가 포함된 그룹의 처리 방식

groupby()의 기준이 되는 컬럼 내에 결측치(NaN)가 포함되어 있다면, 판다스는 기본적으로 해당 데이터 행을 그룹 분류에서 배제(누락)시킵니다. 만약 결측치가 있는 행들도 하나의 ‘미지정’ 혹은 ‘결측그룹’으로 포함하여 빈도를 집계해야 하는 비즈니스 요구사항이 있다면 dropna=False 파라미터를 추가하여 누락 데이터의 분포를 파악해야 합니다.

# 결측치 그룹도 누락하지 않고 집계 결과에 포함
df_with_nan_group = store_df.groupby('지점명', dropna=False)['매출액'].sum()

4. 빅데이터분석기사 실기 시험 환경에서의 실전 활용 포인트

빅데이터분석기사 실기 시험(특히 작업형 제1유형)에서는 특정 그룹별 요약 통계량을 구한 뒤 조건 비교를 수행하는 문제가 높은 빈도로 출제됩니다.

4.1. 그룹별 통계량 기반 특정 타깃 추출

예를 들어 “상품 데이터셋에서 제조사별 평균 가격을 계산한 후, 가장 평균 가격이 높은 제조사의 평균 가격과 가장 낮은 제조사의 평균 가격의 차이를 소수점 둘째 자리까지 구하시오”라는 문제가 있다면 다음과 같이 코드를 구조화할 수 있습니다.

# 1. 제조사별 가격 평균 집계 및 인덱스 초기화
grouped_price = df.groupby('제조사')['가격'].mean().reset_index()

# 2. 최댓값과 최솟값 도출
max_price = grouped_price['가격'].max()
min_price = grouped_price['가격'].min()

# 3. 차이 계산 및 출력
result = max_price - min_price
print(round(result, 2))

4.2. 복합 조건 필터링과의 연계

작업형 제2유형의 데이터 분석 전처리 단계에서도 groupby()를 통해 도출한 ‘고객별 평균 구매액’ 등의 파생변수를 기존 원본 테이블에 결합(merge 또는 transform)하여 모델의 학습 피처로 확장하는 전략이 자주 사용됩니다. 그룹별 집계 결과를 원본 데이터에 결합하는 방법을 이해하면 다양한 피처 엔지니어링 기법을 구현할 수 있으며, 모델 성능 향상에도 도움이 됩니다.

댓글 남기기