본문 바로가기
프로그래밍/Python 관련 정보

[Pandas] Groupby + (Transform/Filter/Apply함수)

by TrillionNT 2025. 2. 5.

2025.02.24 - [프로그래밍/Python 관련 정보] - [Pandas] Table of Contents


Groupby 후 추가적인 기능을 달아서 사용할 수도 있습니다. agg는 Groupby 포스팅에서 상세히 다루었기 때문에 이 부분은 제외하고 나머지 transform, filter, apply에 대해서 알아보겠습니다. 

1. Transform

가끔 데이터프레임에서 연산을 수행하다가 그룹별로 연산을 수행하되 원본 데이터의 행(크기)은 유지한채 남겨두고 싶을 때가 있습니다(=그룹별 연산 후 결과를 원본 row 개수와 동일하게 반환). SQL로 비교하자면 Partition by 를 통해 계산하는 Window함수를 상상하시면 되고, Tableau를 생각하면 VLOD연산에서 Exclude 연산을 생각하시면 되겠습니다. 

 

이러한 작업을 Pandas에서 진행하려면 transform 함수를 사용하면 됩니다. syntax는 아래와 같습니다. 

df['변환된 값'] = df.groupby('그룹')['대상 열'].transform(함수)

 

더보기
import pandas as pd

data = {
    '카테고리': ['A', 'B', 'A', 'B', 'C', 'A', 'C'],
    '값1': [10, 20, 30, 40, 50, 60, 70],
    '값2': [100, 200, 300, 400, 500, 600, 700]
}

df = pd.DataFrame(data)
print(df)

 

  카테고리  값1  값2
0     A  10  100
1     B  20  200
2     A  30  300
3     B  40  400
4     C  50  500
5     A  60  600
6     C  70  700

 

몇 가지 연산 예시를 한번 수행해보겠습니다. 

1) 그룹별 평균 값을 계산

df['카테고리별 값1 평균'] = df.groupby('카테고리')['값1'].transform('mean')
print(df)
  카테고리  값1  값2  카테고리별 값1 평균
0     A  10  100       33.333333
1     B  20  200       30.000000
2     A  30  300       33.333333
3     B  40  400       30.000000
4     C  50  500       60.000000
5     A  60  600       33.333333
6     C  70  700       60.000000

보시는 것처럼 groupby를 시킨 카테고리 별로 같은 값을 가지는 것을 확인할 수 있습니다. 

 

2) 그룹 별 최대값에서의 이격을 계산

df['값1 최대값 차이'] = df.groupby('카테고리')['값1'].transform(lambda x: x.max() - x)
print(df)
  카테고리  값1  값2  값1 최대값 차이
0     A  10  100      50
1     B  20  200      20
2     A  30  300      30
3     B  40  400       0
4     C  50  500      20
5     A  60  600       0
6     C  70  700       0

 

3) 그룹 별 누적 합 계산

df['값1 그룹별 누적 합'] = df.groupby('카테고리')['값1'].transform('cumsum')
print(df)
  카테고리  값1  값2  값1 그룹별 누적 합
0     A  10  100          10
1     B  20  200          20
2     A  30  300          40
3     B  40  400          60
4     C  50  500          50
5     A  60  600         100
6     C  70  700         120

 

 

2. Filter

그룹 별 통계량이 특정 조건을 만족하는 그룹만 추출하고자 할 때 쓰는 기능입니다. 자꾸 SQL에 비유해서 설명하는데, 제가 느끼기엔 이건 SQL group by 후 Having 조건을 주는 것과 비슷하겠네요(물론 Group화 된 상태에서 출력되는 것이 아니라 groupby화 전 필터링이라는 부분이 다르긴 합니다).

더보기
df = pd.DataFrame({
    'dept': ['A', 'A', 'B', 'B', 'B'],
    'sales': [100, 150, 200, 130, 170]
})

df['mean_sales'] = df.groupby('dept')['sales'].transform('mean')
  dept  sales  mean_sales
0    A    100        125.0
1    A    150        125.0
2    B    200        166.6667
3    B    130        166.6667
4    B    170        166.6667
df_filtered = df.groupby('dept').filter(lambda x: x['sales'].mean() > 150)
  dept  sales  mean_sales
2    B    200   166.6667
3    B    130   166.6667
4    B    170   166.6667

 

3. Apply

다들 아시다시피 Groupby와 함께 쓰지 않고 단독으로도 사용되는 기능입니다. 그룹 별로 함수를 적용할 때 그 유연성을 극대화할 수 있는 도구입니다. transform은 shape이 엄격하게 유지된다면 apply의 경우에는 사용 방식에 따라 다양한 결과물이 나올 수 있습니다. 

df_applied = df.groupby('dept').apply(lambda x: x.sort_values('sales', ascending=False))
print(df_applied)
        dept  sales  mean_sales
dept                            
A    1    A    150   125.0
     0    A    100   125.0
B    2    B    200   166.6667
     4    B    170   166.6667
     3    B    130   166.6667

 

 

더보기
df = pd.DataFrame({
    'dept': ['A', 'A', 'B', 'B', 'B'],
    'sales': [100, 150, 200, 130, 170],
    'name': ['Kim', 'Lee', 'Park', 'Choi', 'Jung']
})
  dept  sales   name
0    A    150    Lee
1    A    100    Kim
2    B    200   Park
3    B    170   Jung

예) 그룹별 Top-N뽑기

top2 = df.groupby('dept').apply(lambda x: x.nlargest(2, 'sales')).reset_index(drop=True)
  dept  sales   name
0    A    150    Lee
1    A    100    Kim
2    B    200   Park
3    B    170   Jung