본문으로 건너뛰기
김신건의 로그

[Pandas] groupby

· 수정 · 📖 약 1분 · 503자/단어 #python #pandas #groupby #aggregation
Pandas groupby, groupby 집계, split-apply-combine

정의

DataFrame.groupby(key) 는 데이터를 그룹으로 나누고 (split), 각 그룹에 함수를 적용 (apply), 결과를 합쳐 (combine) 새 DataFrame 으로 만드는 split-apply-combine 패턴의 구현.

SQL 의 GROUP BY 와 같은 개념, 더 풍부한 API.

시각화

기본

df.groupby('city')['salary'].mean()
df.groupby('city')['salary'].sum()
df.groupby('city').agg({'salary': 'mean', 'age': 'max'})
df.groupby(['city', 'dept']).size()
python
import pandas as pd
df = pd.DataFrame({
  'city': ['Seoul', 'Busan', 'Seoul', 'Busan', 'Seoul'],
  'dept': ['A', 'A', 'B', 'B', 'A'],
  'salary': [3000, 4000, 5000, 4500, 3500],
})
print(df.groupby('city')['salary'].mean())
결과
city
Busan    4250.0
Seoul    3833.333333
Name: salary, dtype: float64
citysalary mean
Busan4250.00
Seoul3833.33

여러 컬럼 그룹

df.groupby(['city', 'dept'])['salary'].mean()
python
import pandas as pd
df = pd.DataFrame({
  'city': ['Seoul', 'Busan', 'Seoul', 'Busan', 'Seoul'],
  'dept': ['A', 'A', 'B', 'B', 'A'],
  'salary': [3000, 4000, 5000, 4500, 3500],
})
print(df.groupby(['city', 'dept'])['salary'].mean())
결과
city   dept
Busan  A       4000.0
     B       4500.0
Seoul  A       3250.0
     B       5000.0
Name: salary, dtype: float64

결과는 MultiIndex Series. .reset_index() 또는 .unstack() 으로 평탄화.

agg 로 여러 함수

df.groupby('city').agg({
    'salary': ['mean', 'sum', 'max'],
    'age': 'mean',
})

Pandas agg 참고.

자주 쓰는 메서드

메서드의미
.sum(), .mean(), .median(), .std()통계
.min(), .max()극값
.count()non-null 개수
.size()그룹별 행 수 (NaN 포함)
.first(), .last(), .nth(i)위치 기반
.head(n), .tail(n)그룹 내 상위/하위 N
.cumsum(), .cumcount()누적
.transform(fn)그룹별 변환, 원본 shape 유지
.apply(fn)임의 함수, 가장 유연
.filter(fn)그룹 단위 필터

groupby 결과는 lazy

g = df.groupby('city')      # GroupBy 객체만, 아직 계산 안 함
g.groups                     # {'Seoul': [0, 2, 4], 'Busan': [1, 3]}
g.get_group('Seoul')         # 한 그룹의 DataFrame
list(g)                      # [(name, df), ...]

명시적으로 집계 함수를 호출해야 계산 발생.

그룹별 정렬 후 head (top N per group)

df.sort_values(['city', 'salary'], ascending=[True, False]) \
   .groupby('city').head(2)

각 city 별 salary 상위 2 명.

그룹별 정규화 (transform)

df['salary_z'] = df.groupby('dept')['salary'].transform(
    lambda x: (x - x.mean()) / x.std()
)

각 부서 내에서 z-score, 원본 DataFrame 의 shape 유지.

함정

1. as_index 의 의미

df.groupby('city')['salary'].mean()             # MultiIndex Series
df.groupby('city', as_index=False)['salary'].mean()   # DataFrame, city 가 컬럼

2. NaN 그룹

기본은 NaN 그룹 제외. dropna=False 로 포함 가능.

df.groupby('city', dropna=False)['salary'].mean()

3. groupby + apply 의 느림

df.groupby('x').apply(complex_function)
# apply 는 가장 느림, 가능하면 agg / transform / 벡터 연산

4. SettingWithCopyWarning

group = df.groupby('city').get_group('Seoul')
group['new'] = 1     # ⚠️
group = group.copy()

Named Aggregation (NamedAgg)

pandas 0.25+ 의 명시적 이름 집계. MultiIndex 컬럼 없이 깔끔한 결과.

result = df.groupby('city').agg(
    mean_salary=('salary', 'mean'),
    max_age=('age', 'max'),
    total=('salary', 'sum'),
    count=('name', 'count'),
)
# 컬럼: mean_salary, max_age, total, count

기존 agg({'salary': ['mean', 'sum']}) 의 MultiIndex 컬럼을 회피.

split-apply-combine 시각화

flowchart LR
    Raw["원본 DataFrame"] -->|"groupby(key)"| Split["Split: 그룹 분리"]
    Split -->|"agg / transform"| Apply["Apply: 함수 적용"]
    Apply --> Combine["Combine: 결과 합산"]
    Combine --> Out["집계 DataFrame"]

시계열 groupby: resample

DatetimeIndex 가 있을 때 시간 단위 groupby.

df = df.set_index('date')
df.resample('ME')['revenue'].sum()     # 월별 합계
df.resample('W')['sales'].mean()       # 주별 평균
df.resample('QE')['profit'].agg(['sum', 'mean'])
alias의미
ME월말 (Month End)
QE분기말 (Quarter End)
W주 (Week, 일요일 기준)
D일 (Day)
h시간

pandas-resample 참고.

Categorical 컬럼과 groupby

df['dept'] = pd.Categorical(df['dept'], categories=['A', 'B', 'C'])
result = df.groupby('dept', observed=False)['salary'].mean()
# 등장하지 않은 카테고리도 NaN 으로 포함

observed=False: 등장하지 않은 카테고리도 결과 포함. pandas 2.x 기본값 observed=True.

pandas-categorical 참고.

성능 팁

# 느림: apply (Python loop)
df.groupby('city').apply(lambda g: g['salary'].mean())

# 빠름: 내장 벡터 집계
df.groupby('city')['salary'].mean()

# 매우 큰 데이터: Categorical 지정 + observed=True
df['city'] = df['city'].astype('category')
df.groupby('city', observed=True)['salary'].mean()
방법속도유연성
.sum() / .mean()매우 빠름낮음
.agg(dict)빠름중간
.transform(fn)중간높음
.apply(fn)느림매우 높음

TIP

apply 회피: transform, agg, 벡터 연산으로 대체 가능한지 먼저 확인. pandas-performance 참고.

참고

이 글의 용어 (7개)
[Pandas] 성능 / 메모리 최적화pandas
정의 pandas 의 흔한 성능 함정과 최적화 패턴. 벡터화 + dtype 선택 + 알고리즘 의 조합이 핵심. 사용 상황 | 상황 | 권장 접근 | |:---|:---| | 단순…
[Pandas] agg / aggregatepandas
정의 는 여러 집계 함수를 한 번에 적용 한다. , , 모두에서 사용 가능. 는 같은 함수의 별칭. agg 처리 흐름 사용 상황 | 상황 | 패턴 | |:---|:---| | 전…
[Pandas] Categoricalpandas
정의 Categorical 은 제한된 고유값 만 가질 수 있는 dtype. 내부적으로 정수 코드로 저장되어 메모리 절약 과 속도 향상 을 동시에 달성한다. 고유값 수가 적고(lo…
[Pandas] DataFramepandas
정의 은 2차원 레이블 테이블. 각 열이 , 모든 열이 같은 (행 라벨) 를 공유. SQL 테이블 / Excel 시트 / R data.frame 의 Python 대응체. 구조 시…
[Pandas] pivot_tablepandas
정의 는 의 확장. 집계 함수를 동반 해 중복 (index, columns) 도 처리. Excel 의 피벗 테이블과 가장 가까운 기능. pivottable 처리 흐름 사용 상황 …
[Pandas] resamplepandas
정의 는 시계열 데이터를 다른 빈도 (월별, 일별, 시간별 등) 로 재샘플링하면서 집계한다. 의 시계열 버전. DatetimeIndex (또는 으로 datetime 컬럼) 이 필…
[Pandas] transform / applypandas
정의 | 메서드 | 입력 | 출력 | 용도 | |:---|:---|:---|:---| | | 그룹 | 같은 shape | 정규화, 보충 | | | 그룹 | 임의 shape | 가…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기