지난번 포스팅에서는 Python pandas에서 Group By 집계 시에 grouped.agg()안에 칼럼과 함수를 매핑한 Dict를 사용하여 칼럼별로 특정 GroupBy 집계 함수를 적용하는 방법을 소개하였습니다. 


이번 포스팅에서는 Python pandas에서 Group By 집계 시에 grouped.apply()를 사용하여 여러개의 칼럼(Multiple Columns)에 대해서 각기 다른 함수들을 적용하는 방법을 소개하겠습니다. 지난번 포스팅과 비교를 해보시고, 편리하거나 이해하기 쉽다고 느껴지는 방법을 사용하면 되겠습니다. 


(1) 데이터프레임에서 여러개의 칼럼에 대해 다른 함수 적용하여 Group By 집계하기

    : grouped.apply(function)


(2) 계층적 인덱스를 가진 데이터프레임의 여러개의 칼럼에 대해 다른 함수 적용하여 Group By 집계하기

    : grouped(level=['index1', 'index2']).apply(function)






  (1) 데이터프레임에서 여러개의 칼럼에 대해 다른 함수 적용하여 Group By 집계하기

      : grouped.apply(function)



예제로 사용할 간단한 데이터프레임을 만들어보겠습니다. 



import numpy as np

import pandas as pd


df = pd.DataFrame({'grp_col_1' : ['a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b'], 

                   'grp_col_2' : ['c', 'c', 'd', 'd', 'd', 'e', 'e', 'f', 'f', 'f'], 

                   'val_1' : np.arange(10),                   

                   'val_2' : np.random.randn(10)})

 

df

grp_col_1grp_col_2val_1val_2
0ac00.829698
1ac1-0.766809
2ad2-2.119938
3ad30.885744
4ad4-1.135036
5be50.126890
6be62.755351
7bf70.441467
8bf8-1.166549
9bf9-0.712455





위에서 만든 데이터프레임의 'val_1' 칼럼에 대해서 평균(mean)과 표준편차(std)를 구하고, 'val_2' 칼럼에 대해서는 최대값(max)과 최소값(min), 그리고 범위(range)를 구하는 사용자 정의 함수 func()를 정의해보겠습니다. 처음에 빈 Dict 를 선언하고, 각 칼럼별 함수의 이름(key)과 함수를 적용한 결과(value)를 Dict의 key와 value로 매핑한 후에, 마지막에 pandas Series 로 반환하는 사용자 정의 함수입니다.



def func(x):

    d = {}

    d['val_1_mean'] = x['val_1'].mean()

    d['val_1_std'] = x['val_1'].std()

    d['val_2_max'] = x['val_2'].max()

    d['val_2_min'] = x['val_2'].min()

    d['val_2_range'] = x['val_2'].max() - x['val_2'].min()

    return pd.Series(d, index=['val_1_mean', 'val_1_std', 'val_2_max', 'val_2_min', 'val_2_range'])

 




위에서 정의한 사용자 정의 함수 func()를 Group By 집계 시 grouped.apply(func) 처럼 apply() 괄호 안에 함수이름을 써주면 됩니다. Group By 집계에 'grp_col_1', 'grp_col_2'의 두개 변수를 사용하였으므로 아래의 결과처럼 계층이 있는 인덱스(Hierarchical Index)를 가진 데이터프레임이 반환되었습니다. 



df_return = df.groupby(['grp_col_1', 'grp_col_2']).apply(func)

df_return


val_1_meanval_1_stdval_2_maxval_2_minval_2_range
grp_col_1grp_col_2
ac0.50.7071070.829698-0.7668091.596508
d3.01.0000000.885744-2.1199383.005682
be5.50.7071072.7553510.1268902.628461
f8.01.0000000.441467-1.1665491.608016

 




계층적 인덱스를 사용하고 싶지 않다면 reset_index() 로 인덱스를 칼럼으로 변환해주면 됩니다. 



df_return.reset_index()

grp_col_1grp_col_2val_1_meanval_1_stdval_2_maxval_2_minval_2_range
0ac0.50.7071070.829698-0.7668091.596508
1ad3.01.0000000.885744-2.1199383.005682
2be5.50.7071072.7553510.1268902.628461
3bf8.01.0000000.441467-1.1665491.608016

 




계층적 인덱스 전부 말고 일부만 선별해서 칼럼으로 변환을 하고 싶으면 reset_index(level='index_name') 처럼 reset_index() 의 안에 level 을 지정해주면 됩니다. 



df_return.reset_index(level='grp_col_2')

grp_col_2val_1_meanval_1_stdval_2_maxval_2_minval_2_range
grp_col_1
ac0.50.7071070.829698-0.7668091.596508
ad3.01.0000000.885744-2.1199383.005682
be5.50.7071072.7553510.1268902.628461
bf8.01.0000000.441467-1.1665491.608016

 




계층적 인덱스를 아예 삭제하고 싶으면 reset_index(drop=True) 처럼 drop=True 를 추가해주면 됩니다. (물론, 이렇게하면 Group By가 무엇을 기준으로 되었는지 파악이 불가능하기 때문에 사용하면 안될거 같긴 합니다. ^^;;;)



df_return.reset_index(drop=True)

val_1_meanval_1_stdval_2_maxval_2_minval_2_range
00.50.7071070.829698-0.7668091.596508
13.01.0000000.885744-2.1199383.005682
25.50.7071072.7553510.1268902.628461
38.01.0000000.441467-1.1665491.608016

 




Group By로 집계되어 반환된 객체가 데이터프레임으로 특정 칼럼만 선택할 수 있습니다. 예를 들어, 'val_1_mean', 'val_1_std'의 두 개 칼럼만 선택해보면 아래와 같습니다. 



df_return[['val_1_mean', 'val_1_std']]

val_1_meanval_1_std
grp_col_1grp_col_2
ac0.50.707107
d3.01.000000
be5.50.707107
f8.01.000000

 





  (2) 계층적 인덱스를 가진 데이터프레임의 여러개의 칼럼에 대해 다른 함수 적용하여 Group By 집계하기

      : grouped(level=['index1', 'index2']).apply(function)


위의 (1)번 예에서 들었던 데이터프레임과 'val_1', 'val_2' 의 숫자형 변수와 값은 동일하며, Group By 집계를 하는 기준이 되는 'grp_col_1', 'grp_col_2' 변수는 'grp_idx_1', 'grp_idx_2' 라는 이름의 인덱스 이름으로 하여 계층적 인덱스로 하여 데이터프레임을 만들어보겠습니다. 


pd.MultiIndex.from_arrays() 를 사용하여 계층적 인덱스를 만들어줍니다. (R이나 SQL을 사용하다가 Python 의 계층적 인덱스를 처음 보면 '이게 뭐지?' 싶었는데요, 자꾸 써보니 나름 편리합니다)


pd.DataFrame() 으로 데이터프레임을 만들 때 index 에  pd.MultiIndex.from_arrays() 로 만든 인덱스를 할당해주면 됩니다. 



# How to make a DataFrame with Hierarchical Index

arrays = [['a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b'], 

         ['c', 'c', 'd', 'd', 'd', 'e', 'e', 'f', 'f', 'f']]


myindex = pd.MultiIndex.from_arrays(arrays, names=('grp_idx_1', 'grp_idx_2'))

 


df2 = pd.DataFrame({'val_1': np.arange(10), 

                    'val_2': np.random.randn(10)}, 

                   index = myindex)


df2

val_1val_2
grp_idx_1grp_idx_2
ac0-0.491750
c10.507519
d20.099639
d30.669201
d40.714737
be5-0.865795
e6-1.174955
f70.878559
f8-0.877475
f9-0.323399

 




계층적 인덱스를 가진 데이터프레임에 대해 Group By 집계를 할 때는 groupby(level=['index_1', 'index_2']).apply(function) 의 형태로 지정을 해주면 됩니다. groupby() 로 집계 기준을 설정하고 apply(function)으로 함수를 지정해주는 것은 위의 (1)번과 같은데요, groupby()의 안에 level=['index_1', 'index_2'] 처럼 입력하는 부분이 다릅니다. 



df2.groupby(level=['grp_idx_1', 'grp_idx_2']).apply(func)

val_1_meanval_1_stdval_2_maxval_2_minval_2_range
grp_idx_1grp_idx_2
ac0.50.7071070.507519-0.4917500.999269
d3.01.0000000.7147370.0996390.615099
be5.50.707107-0.865795-1.1749550.309161
f8.01.0000000.878559-0.8774751.756035

 



많은 도움이 되었기를 바랍니다. 

Posted by R Friend R_Friend