groupby 코드 예시

김신영·2025년 10월 11일
0
post-thumbnail

가장 자주 쓰는 건 agg(), size(), transform(), unstack()

1. 집계 함수 (Aggregation)

size() - 그룹 크기

# 각 그룹의 행 개수
df.groupby('category').size()

# 예시
sales = pd.DataFrame({
    'category': ['A', 'A', 'B', 'B', 'B'],
    'amount': [100, 200, 150, 300, 250]
})
sales.groupby('category').size()
# category
# A    2
# B    3

count() - Non-NaN 개수

# 각 컬럼의 non-NaN 개수
df.groupby('category').count()

# NaN이 있는 경우
df = pd.DataFrame({
    'category': ['A', 'A', 'B', 'B'],
    'value': [1, None, 3, 4]
})
df.groupby('category').count()
# category  value
# A             1  (NaN 제외)
# B             2

sum() - 합계

# 각 그룹의 합
df.groupby('category')['amount'].sum()

# 여러 컬럼
df.groupby('category')[['amount', 'quantity']].sum()

# 예시
sales.groupby('category')['amount'].sum()
# category
# A    300
# B    700

mean() - 평균

# 평균
df.groupby('category')['amount'].mean()

sales.groupby('category')['amount'].mean()
# category
# A    150.0
# B    233.3

max(), min() - 최대/최소

# 최대값
df.groupby('category')['amount'].max()

# 최소값
df.groupby('category')['amount'].min()

# 여러 통계 한 번에
df.groupby('category')['amount'].agg(['min', 'max', 'mean'])

median(), std(), var() - 중앙값, 표준편차, 분산

# 중앙값
df.groupby('category')['amount'].median()

# 표준편차
df.groupby('category')['amount'].std()

# 분산
df.groupby('category')['amount'].var()

2. agg() - 여러 집계 함수

단일 컬럼, 여러 함수

df.groupby('category')['amount'].agg(['sum', 'mean', 'count', 'std'])

# 결과:
# category  sum   mean  count   std
# A         300  150.0      2  70.7
# B         700  233.3      3  76.4

여러 컬럼, 각각 다른 함수

df.groupby('category').agg({
    'amount': ['sum', 'mean'],
    'quantity': ['count', 'max'],
    'price': 'median'
})

커스텀 함수

# 사용자 정의 함수
df.groupby('category')['amount'].agg(lambda x: x.max() - x.min())

# 이름 지정
df.groupby('category')['amount'].agg(
    total='sum',
    average='mean',
    range=lambda x: x.max() - x.min()
)

3. transform() - 그룹별 변환

# 각 그룹의 평균을 원본 크기로 브로드캐스트
df['group_mean'] = df.groupby('category')['amount'].transform('mean')

# 예시
sales['category_avg'] = sales.groupby('category')['amount'].transform('mean')
print(sales)
#   category  amount  category_avg
# 0        A     100         150.0
# 1        A     200         150.0
# 2        B     150         233.3
# 3        B     300         233.3
# 4        B     250         233.3

# 그룹 평균으로 정규화
df['normalized'] = df.groupby('category')['amount'].transform(
    lambda x: (x - x.mean()) / x.std()
)

4. apply() - 그룹별 커스텀 함수

# 각 그룹에 함수 적용
df.groupby('category').apply(lambda x: x.nlargest(2, 'amount'))

# 복잡한 연산
def custom_func(group):
    return pd.Series({
        'total': group['amount'].sum(),
        'count': len(group),
        'avg': group['amount'].mean()
    })

df.groupby('category').apply(custom_func)

5. filter() - 그룹 필터링

# 조건을 만족하는 그룹만 유지
# 평균이 200 이상인 그룹만
df.groupby('category').filter(lambda x: x['amount'].mean() > 200)

# 예시: 2개 이상의 항목이 있는 그룹만
df.groupby('category').filter(lambda x: len(x) >= 2)

6. first(), last() - 첫/마지막 항목

# 각 그룹의 첫 번째 행
df.groupby('category').first()

# 마지막 행
df.groupby('category').last()

# 특정 컬럼만
df.groupby('category')['date'].first()

7. nth() - n번째 항목

# 각 그룹의 첫 번째 항목
df.groupby('category').nth(0)

# 두 번째 항목
df.groupby('category').nth(1)

# 마지막 항목
df.groupby('category').nth(-1)

# 여러 개
df.groupby('category').nth([0, -1])  # 첫 번째와 마지막

8. cumsum(), cumcount() - 누적 계산

# 그룹 내 누적 합
df['cumsum'] = df.groupby('category')['amount'].cumsum()

# 그룹 내 순번 (0부터 시작)
df['row_num'] = df.groupby('category').cumcount()

# 그룹 내 누적 개수
df['cumcount'] = df.groupby('category')['amount'].cumcount() + 1

# 예시
sales['cumulative'] = sales.groupby('category')['amount'].cumsum()
sales['rank'] = sales.groupby('category').cumcount() + 1
print(sales)
#   category  amount  cumulative  rank
# 0        A     100         100     1
# 1        A     200         300     2
# 2        B     150         150     1
# 3        B     300         450     2
# 4        B     250         700     3

9. rank() - 그룹 내 순위

# 그룹 내 순위
df['rank'] = df.groupby('category')['amount'].rank(ascending=False)

# 순위 방법 지정
df['rank'] = df.groupby('category')['amount'].rank(method='dense')

# method 옵션:
# - 'average': 동점자 평균 순위
# - 'min': 동점자 최소 순위
# - 'max': 동점자 최대 순위
# - 'first': 먼저 나온 순서
# - 'dense': 빈틈없는 순위

10. shift() - 그룹 내 이동

# 그룹 내에서 이전 값
df['prev_amount'] = df.groupby('category')['amount'].shift(1)

# 다음 값
df['next_amount'] = df.groupby('category')['amount'].shift(-1)

# 차이 계산
df['diff'] = df.groupby('category')['amount'].diff()

11. nlargest(), nsmallest() - 상위/하위 n개

# 각 그룹에서 상위 2개
df.groupby('category').apply(lambda x: x.nlargest(2, 'amount'))

# 각 그룹에서 하위 1개
df.groupby('category').apply(lambda x: x.nsmallest(1, 'amount'))

12. get_group() - 특정 그룹 추출

grouped = df.groupby('category')

# 특정 그룹만 가져오기
group_a = grouped.get_group('A')
print(group_a)

실무 종합 예제

1. 쿼리별 통계

import pandas as pd

# 샘플 데이터
qrels = pd.DataFrame({
    'query_id': [1, 1, 1, 2, 2, 3, 3, 3, 3],
    'doc_id': [101, 102, 103, 201, 202, 301, 302, 303, 304],
    'relevance': [3, 2, 1, 3, 0, 2, 2, 1, 3]
})

# 쿼리별 통계
query_stats = qrels.groupby('query_id')['relevance'].agg([
    ('count', 'count'),
    ('mean', 'mean'),
    ('max', 'max'),
    ('min', 'min'),
    ('std', 'std')
]).round(2)

print(query_stats)
#           count  mean  max  min   std
# query_id                              
# 1             3  2.00    3    1  1.00
# 2             2  1.50    3    0  2.12
# 3             4  2.00    3    1  0.82

2. 레이블별 분포

train_qrels = pd.DataFrame({
    'query_id': [1, 1, 1, 2, 2, 3],
    'doc_id': [101, 102, 103, 201, 202, 301],
    'label': ['E', 'S', 'C', 'E', 'E', 'S']
})

# 레이블별 개수
label_dist = train_qrels.groupby('label').size()
print(label_dist)
# label
# C    1
# E    3
# S    2

# 쿼리-레이블 조합
query_label = train_qrels.groupby(['query_id', 'label']).size().unstack(fill_value=0)
print(query_label)
# label      C  E  S
# query_id            
# 1          1  1  1
# 2          0  2  0
# 3          0  0  1

3. 시계열 데이터 처리

logs = pd.DataFrame({
    'user_id': [1, 1, 1, 2, 2, 2],
    'timestamp': pd.date_range('2025-01-01', periods=6, freq='H'),
    'action': ['login', 'view', 'logout', 'login', 'click', 'logout']
})

# 사용자별 첫 로그인 시간
logs.groupby('user_id')['timestamp'].first()

# 사용자별 활동 개수
logs.groupby('user_id').size()

# 사용자별 마지막 활동
logs.groupby('user_id').last()

4. 윈도우 함수 스타일

sales = pd.DataFrame({
    'date': pd.date_range('2025-01-01', periods=10),
    'category': ['A', 'B'] * 5,
    'amount': [100, 150, 200, 180, 120, 160, 140, 190, 110, 170]
})

# 카테고리별 누적 합
sales['cumsum'] = sales.groupby('category')['amount'].cumsum()

# 카테고리별 이동 평균 (3일)
sales['rolling_avg'] = sales.groupby('category')['amount'].transform(
    lambda x: x.rolling(3, min_periods=1).mean()
)

# 카테고리 내 순위
sales['rank'] = sales.groupby('category')['amount'].rank(ascending=False)

print(sales)

5. 조건부 집계

# 조건을 만족하는 행만 집계
df.groupby('category').apply(
    lambda x: x[x['amount'] > 100]['amount'].sum()
)

# 그룹별 최빈값
df.groupby('category')['product'].apply(lambda x: x.mode()[0])

# 그룹별 고유값 개수
df.groupby('category')['product'].nunique()

자주 쓰는 패턴 요약

# 1. 기본 집계
df.groupby('col').agg(['sum', 'mean', 'count'])

# 2. 여러 컬럼, 각각 다른 함수
df.groupby('col').agg({'col1': 'sum', 'col2': 'mean'})

# 3. 그룹 크기
df.groupby('col').size()

# 4. 피벗 테이블 스타일
df.groupby(['col1', 'col2']).size().unstack(fill_value=0)

# 5. 그룹별 변환 (원본 크기 유지)
df.groupby('col')['value'].transform('mean')

# 6. 그룹별 순위
df.groupby('col')['value'].rank(ascending=False)

# 7. 그룹별 누적
df.groupby('col')['value'].cumsum()

# 8. 상위 n개
df.groupby('col').apply(lambda x: x.nlargest(3, 'value'))

# 9. 조건 필터링
df.groupby('col').filter(lambda x: len(x) >= 5)

# 10. 커스텀 함수
df.groupby('col').apply(custom_function)
profile
Hello velog!

0개의 댓글