Groupby
Contents
Groupby#
Groupby 는 데이터를 요약할 때 많이 활용하는 기법입니다. 아래 예제에서 만들어진 DataFrame - df 의 ‘grp’ 컬럼을 이용하여 ‘a’, ‘b’, ‘c’ 등의 3 개의 그룹으로 나눌 수 있습니다. 먼저, 그룹을 무시하고 v1, v2 의 평균값을 알아봅니다. 그 다음, 그룹 별로 v1 과 v2 의 평균값을 알아봅니다.
import pandas as pd
g_list = ['a','a','a','b','b','b','c','c','c','c']
v1_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
v2_list = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
df = pd.DataFrame({'grp': g_list, 'v1': v1_list, 'v2': v2_list}) # 그룹핑을 할 수 있는 컬럼을 가진 DataFrame 생성
df[['v1', 'v2']].mean() # 전체 평균
v1 5.5
v2 15.5
dtype: float64
df.groupby('grp')['v1'].mean() # 그룹별 평균
grp
a 2.0
b 5.0
c 8.5
Name: v1, dtype: float64
그룹별로 여러개의 통계값도 구할 수 있습니다. v1 의 평균, 최대값, 총합을 알아봅니다.
df.groupby('grp')['v1'].agg(['mean','max','sum'])
mean | max | sum | |
---|---|---|---|
grp | |||
a | 2.0 | 3 | 6 |
b | 5.0 | 6 | 15 |
c | 8.5 | 10 | 34 |
그룹별로 v1 과 v2 의 평균, 최대값, 총합을 알아봅니다.
df.groupby('grp')[['v1','v2']].agg(['mean','max','sum'])
v1 | v2 | |||||
---|---|---|---|---|---|---|
mean | max | sum | mean | max | sum | |
grp | ||||||
a | 2.0 | 3 | 6 | 12.0 | 13 | 36 |
b | 5.0 | 6 | 15 | 15.0 | 16 | 45 |
c | 8.5 | 10 | 34 | 18.5 | 20 | 74 |
이번에는 그룹별로 v1 은 평균, v2 는 총합을 알아봅니다.
s = {'v1':'mean', 'v2':'sum'}
df.groupby('grp').agg(s)
v1 | v2 | |
---|---|---|
grp | ||
a | 2.0 | 36 |
b | 5.0 | 45 |
c | 8.5 | 74 |
그룹별 최대값에서 최소값을 뺀 값을 알아봅니다. lambda 함수를 이용했습니다. lambda 함수의 자세한 활용법은 다루지 않도록 하겠습니다. Apply 함수를 이용한 경우와 Transform 함수를 이용한 경우의 차이점을 알아야 합니다. Apply 를 이용하면 생성된 그룹의 갯 수 만큼의 행을 리턴합니다. Transform 은 그룹핑하기 전의 데이터 행의 갯 수 만큼을 반환합니다. 그룹별 요약된 정보를 원래 데이터에 추가하고 싶을 때는 Transform 이 사용됩니다.
df.groupby('grp')['v1'].apply(lambda x: x.max() - x.mean())
grp
a 1.0
b 1.0
c 1.5
Name: v1, dtype: float64
df.groupby('grp')['v1'].transform(lambda x: x.max() - x.mean())
0 1.0
1 1.0
2 1.0
3 1.0
4 1.0
5 1.0
6 1.5
7 1.5
8 1.5
9 1.5
Name: v1, dtype: float64
pd.cut / pd.qcut#
이번에는 그룹핑을 하기 위해 활용되는 pd.qcut() 혹은 pd.cut() 메소드에 대하여 알아보겠습니다. 어떤 변수를 그룹 별로 분석하고 싶습니다. 예를 들어, 섹터가 그룹이라면 groupby(‘섹터’)[‘수익률’].mean() 명령으로 섹터별로 각 섹터에 속하는 종목들의 수익율 평균울 구할 수 있습니다. 하지만 그룹 변수가 없고 연속형 변수를 구간으로 나누어 그룹화하고 싶은 경우 pd.cut() 나 pd.qcut() 을 이용합니다. pd.cut 은 직접 구간을 지정해 그룹을 만들고, pd.qcut 은 분위 수를 이용하여 구간을 만듭니다.
pd.qcut(Series, q=10) # 십 분위수로 구간 생성
pd.cut(Series, bins=[a1, a2, a3]) # bins 인수를 이용하면 (a1, a2], (a2, a3]로 구간 생성
np.arange(100) 을 이용하여 0 부터 99까지 값을 생성한 후 pd.qcut 와 pd.cut 을 사용 해 보겠습니다.
import numpy as np
import pandas as pd
a_list = np.arange(100)
df = pd.DataFrame({'a': a_list})
df.head()
a | |
---|---|
0 | 0 |
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
10 이하의 숫자와 90 초과의 숫자는 해댱 구간이 없어서 그룹핑이 되지 않았습니다.
rank = pd.cut(df['a'], bins=[10, 25, 75, 90])
df.groupby(rank)['a'].agg(['min','max','count'])
min | max | count | |
---|---|---|---|
a | |||
(10, 25] | 11 | 25 | 15 |
(25, 75] | 26 | 75 | 50 |
(75, 90] | 76 | 90 | 15 |
bins 구간이 모든 값을 포함하도록 하기 위해서는 아래와 같이 bins 를 설정합니다.
rank = pd.cut(df['a'], bins=[-np.inf, 10, 25, 75, 90, np.inf])
df.groupby(rank).agg(['min','max','count'])
a | |||
---|---|---|---|
min | max | count | |
a | |||
(-inf, 10.0] | 0 | 10 | 11 |
(10.0, 25.0] | 11 | 25 | 15 |
(25.0, 75.0] | 26 | 75 | 50 |
(75.0, 90.0] | 76 | 90 | 15 |
(90.0, inf] | 91 | 99 | 9 |
이번에는 제가 주로 활용하는 pd.qcut 입니다. pd.cut 은 bins 로 구간을 설정해야 하나, qcut 는 q 인수로 분위수를 이용하여 구간을 만듭니다. 아래 결과를 보시면 q 의 역할을 아실 수 있을 것이라고 생각합니다.
rank = pd.qcut(df['a'], q=10)
df.groupby(rank)['a'].agg(['min','max','count'])
min | max | count | |
---|---|---|---|
a | |||
(-0.001, 9.9] | 0 | 9 | 10 |
(9.9, 19.8] | 10 | 19 | 10 |
(19.8, 29.7] | 20 | 29 | 10 |
(29.7, 39.6] | 30 | 39 | 10 |
(39.6, 49.5] | 40 | 49 | 10 |
(49.5, 59.4] | 50 | 59 | 10 |
(59.4, 69.3] | 60 | 69 | 10 |
(69.3, 79.2] | 70 | 79 | 10 |
(79.2, 89.1] | 80 | 89 | 10 |
(89.1, 99.0] | 90 | 99 | 10 |
rank = pd.qcut(df['a'], q=5)
df.groupby(rank)['a'].agg(['min','max','count'])
min | max | count | |
---|---|---|---|
a | |||
(-0.001, 19.8] | 0 | 19 | 20 |
(19.8, 39.6] | 20 | 39 | 20 |
(39.6, 59.4] | 40 | 59 | 20 |
(59.4, 79.2] | 60 | 79 | 20 |
(79.2, 99.0] | 80 | 99 | 20 |