이번 포스팅에서는 Pandas DataFrame에서 groupby() 를 사용해 그룹 단위로 요약 통계량(group-level statistics aggregation)을 집계하여 원래의 DataFrame에 transform() 함수를 사용하여 통계량 칼럼을 추가하는 방법을 소개하겠습니다. 




예제로 사용할 간단한 Pandas DataFrame을 만들어보겠습니다. 



 import numpy as np

 import pandas as pd

 from pandas import DataFrame


 df = DataFrame({'group_1': ['a', 'a', 'a', 'a', 'a',  

                                      'b', 'b', 'b', 'b', 'b',], 

                       'group_2': ['c', 'c', 'c', 'd', 'd', 

                                     'e', 'e', 'e', 'f', 'f'], 

                       'col': [1, 2, np.NaN, 4, np.NaN, 

                              6, 7, np.NaN, 9, 10]})

 

 df

colgroup_1group_2
01.0ac
12.0ac
2NaNac
34.0ad
4NaNad
56.0be
67.0be
7NaNbe
89.0bf
910.0bf




위의 DataFrame에 대해서 


(1)  GroupBy() 로 'group_1'과 'group_2' 칼럼을 모두 적용한 그룹을 만들어서
     [그룹 ('a', 'c'), 그룹 ('a', 'd'), 그룹 ('b'e, 'e'), 그룹 ('b', 'f')]


(2) 그룹별로 transform() 함수를 사용하여 NaN이 아닌 원소의 '개수(count)', '합(sum)', '최대값(max)'을 계산하여 


(3) df의 DataFrame에 새로운 칼럼을 추가해보겠습니다. 



  (a) 그룹 별 NaN이 아닌 원소의 개수 구하여 데이터프레임에 새로운 칼럼 추가하기



# 'count' grouped by (['group_1', 'group_2'])

 df['count_col'] = df.groupby(['group_1', 'group_2']).col.transform('count')

 df

col

group_1group_2

count_col

01.0ac2.0
12.0ac2.0
2NaNac2.0
34.0ad1.0
4NaNad1.0
56.0be2.0
67.0be2.0
7NaNbe2.0
89.0bf2.0
910.0bf2.0

 




  (b) 그룹 별 NaN이 아닌 원소의 합을 구하여 데이터프레임에 새로운 칼럼 추가하기



 # 'sum' grouped by (['group_1', 'group_2'])

 df['sum_col'] = df.groupby(['group_1', 'group_2']).col.transform('sum')

 df

colgroup_1group_2count_colsum_col
01.0ac2.03.0
12.0ac2.03.0
2NaNac2.03.0
34.0ad1.04.0
4NaNad1.04.0
56.0be2.013.0
67.0be2.013.0
7NaNbe2.013.0
89.0bf2.019.0
910.0bf2.019.0





  (c) 그룹 별 NaN이 아닌 원소 중 최대값을 구하여 데이터프레임에 새로운 칼럼 추가하기



 # 'max' grouped by (['group_1', 'group_2'])

 df['max_col'] = df.groupby(['group_1', 'group_2']).col.transform('max')

 df

colgroup_1group_2count_colsum_colmax_col
01.0ac2.03.02.0
12.0ac2.03.02.0
2NaNac2.03.02.0
34.0ad1.04.04.0
4NaNad1.04.04.0
56.0be2.013.07.0
67.0be2.013.07.0
7NaNbe2.013.07.0
89.0bf2.019.010.0
910.0bf2.019.010.0




이번 포스팅에서는 groupby() 연산자와 transform('statistics function') 함수를 사용하여 그룹별로 통계량을 계산하여 기존의 DataFrame에 새로운 그룹별 집계된 통계량을 새로운 변수로 추가하는 방법에 대해서 알아보았습니다. 


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


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. ^^



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 groupby() 연산자를 사용하여 집단, 그룹별로 데이터를 집계, 요약하는 방법을 소개하겠습니다. 


전체 데이터를 그룹 별로 나누고 (split), 각 그룹별로 집계함수를 적용(apply) 한후, 그룹별 집계 결과를 하나로 합치는(combine) 단계를 거치게 됩니다. (Split => Apply function => Combine)



[ GroupBy aggregation mechanics ]




groupby() 는 다양한 변수를 가진 데이터셋을 분석하는데 있어 그룹별로 데이터를 집계하는 분석은 일상적으로 이루어지는 만큼 사용빈도가 매우 높고 알아두면 유용합니다. 


실습에 사용할 예제는 바다 해산물인 전복(abalone)에 대한 공개 데이터셋을 사용하겠습니다. 



[ UCI Machine Learning Repository ]

  • Abalone CSV dataset download:  abalone.txt
  • Variables

    Name Data Type Meas. Description ---- --------- ----- ----------- Sex nominal M, F, and I (infant) Length continuous mm Longest shell measurement Diameter continuous mm perpendicular to length Height continuous mm with meat in shell Whole weight continuous grams whole abalone Shucked weight continuous grams weight of meat Viscera weight continuous grams gut weight (after bleeding) Shell weight continuous grams after being dried Rings integer +1.5 gives the age in years



먼저, 바로 위에 링크해놓은 abalone.txt를 다운받은 후에, abalone.txt 데이터셋을 pandas의 read_csv() 로 불러와서 DataFrame을 만들어보겠습니다. 



# Importing common libraries

import pandas as pd

from pandas import DataFrame

from pandas import Series

import numpy as np

 

# Reading abalone data set

abalone = pd.read_csv("/Users/ihongdon/Documents/Python/abalone.txt", 

                      sep=",", 

                      names = ['sex', 'length', 'diameter', 'height', 

                               'whole_weight', 'shucked_weight', 'viscera_weight', 

                               'shell_weight', 'rings'], 

                      header = None)





abalone 라는 이름의 pandas DataFrame을 만들었으니, 데이터가 어떻게 생겼는지 탐색해보겠습니다. 다행히 결측치는 없으며, 4,177개의 관측치를 가지고 있네요. 전복의 성별(sex) 변수가 범주형 변수입니다. 



# View of top 5 observations

abalone.head()


sex length diameter height whole_weight shucked_weight viscera_weight shell_weight rings

0 M 0.455 0.365 0.095 0.5140 0.2245 0.1010 0.150 15

1 M 0.350 0.265 0.090 0.2255 0.0995 0.0485 0.070 7

2 F 0.530 0.420 0.135 0.6770 0.2565 0.1415 0.210 9

3 M 0.440 0.365 0.125 0.5160 0.2155 0.1140 0.155 10

4 I 0.330 0.255 0.080 0.2050 0.0895 0.0395 0.055 7



# Check the missing value

np.sum(pd.isnull(abalone))


sex               0
length            0
diameter          0
height            0
whole_weight      0
shucked_weight    0
viscera_weight    0
shell_weight      0
rings             0
dtype: int64

 


# Descriptive statics

abalone.describe()

lengthdiameterheightwhole_weightshucked_weightviscera_weightshell_weightrings
count4177.0000004177.0000004177.0000004177.0000004177.0000004177.0000004177.0000004177.000000
mean0.5239920.4078810.1395160.8287420.3593670.1805940.2388319.933684
std0.1200930.0992400.0418270.4903890.2219630.1096140.1392033.224169
min0.0750000.0550000.0000000.0020000.0010000.0005000.0015001.000000
25%0.4500000.3500000.1150000.4415000.1860000.0935000.1300008.000000
50%0.5450000.4250000.1400000.7995000.3360000.1710000.2340009.000000
75%0.6150000.4800000.1650001.1530000.5020000.2530000.32900011.000000
max0.8150000.6500001.1300002.8255001.4880000.7600001.00500029.000000





자, 데이터 준비가 되었으니 이제부터 '전복 성별(sex)' 그룹('F', 'M', 'I')별로 전복의 전체 무게('whole_weight') 변수에 대해서 GroupBy 집계를 해보겠습니다. 


집단별 크기는 grouped.size(), 집단별 합계는 grouped.sum(), 집단별 평균은 grouped.mean() 을 사용합니다. 



grouped = abalone['whole_weight'].groupby(abalone['sex'])


grouped 

<pandas.core.groupby.SeriesGroupBy object at 0x112668c10>


grouped.size()

sex
F    1307
I    1342
M    1528
Name: whole_weight, dtype: int64


grouped.sum()

sex
F    1367.8175
I     578.8885
M    1514.9500
Name: whole_weight, dtype: float64


grouped.mean()

sex
F    1.046532
I    0.431363
M    0.991459
Name: whole_weight, dtype: float64





위의 예에서는 'whole_weight' 라는 하나의 연속형 변수에 대해서만 '성별(sex)' 집계를 하였습니다만, 집계를 하는 key를 제외한 데이터프레임 안의 전체 연속형 변수에 대해서 한꺼번에 집계를 할 수도 있습니다. 



abalone.groupby(abalone['sex']).mean()

lengthdiameterheightwhole_weightshucked_weightviscera_weightshell_weightrings
sex
F0.5790930.4547320.1580111.0465320.4461880.2306890.30201011.129304
I0.4277460.3264940.1079960.4313630.1910350.0920100.1281827.890462
M0.5613910.4392870.1513810.9914590.4329460.2155450.28196910.705497




DataFrame.groupby('key_var').func() 형식으로도 사용가능하며, 위의 abalone.groupby(abalone['sex']).mean()은 아래처럼 abalone.groupby('sex').mean() 처럼 써도 똑같은 결과를 얻을 수 있습니다. 



abalone.groupby('sex').mean()

lengthdiameterheightwhole_weightshucked_weightviscera_weightshell_weightrings
sex
F0.5790930.4547320.1580111.0465320.4461880.2306890.30201011.129304
I0.4277460.3264940.1079960.4313630.1910350.0920100.1281827.890462
M0.5613910.4392870.1513810.9914590.4329460.2155450.28196910.705497





이제부터는 '성별(sex)'  이외에 '길이(length)'를 가지고 범주형 변수를 하나 더 만들어서, 2개의 범주형 변수 key 값을 가지고 그룹별 집계를 해보겠습니다. 


np.where()  함수를 사용하여 length 의 중앙값보다 크면 'length_long'으로, 중앙값보다 작으면 'length_short'의 이름으로하는 계급으로하는 새로운 범주형 변수를 만들어보겠습니다. 



abalone['length_cat'] = np.where(abalone.length > np.median(abalone.length), 

                                 'length_long', # True

                                 'length_short') # False


abalone[['length', 'length_cat']][:10]


  length length_cat

0 0.455 length_short

1 0.350 length_short

2 0.530 length_short

3 0.440 length_short

4 0.330 length_short

5 0.425 length_short

6 0.530 length_short

7 0.545 length_short

8 0.475 length_short

9 0.550 length_long





그럼, 이제 성별 그룹(sex)과 길이 범주(length_cat) 그룹별로  GroupBy 를 사용하여 평균을 구해보겠습니다. 



mean_by_sex_length = abalone['whole_weight'].groupby([abalone['sex'], abalone['length_cat']]).mean()

 

mean_by_sex_length


sex  length_cat  
F    length_long     1.261330
     length_short    0.589702
I    length_long     0.923215
     length_short    0.351234
M    length_long     1.255182
     length_short    0.538157
Name: whole_weight, dtype: float64




위의 집계 결과가 SQL로 집계했을 때의 형태로 결과가 제시가 되었는데요, unstack() 함수를 사용하면 집계 결과를 가로, 세로 축으로 좀더 보기에 좋게 표현을 할 수 있습니다. 



mean_by_sex_length.unstack()

length_catlength_longlength_short
sex
F1.2613300.589702
I0.9232150.351234
M1.2551820.538157

 




abalone['whole_weight'].groupby([abalone['sex'], abalone['length_cat']]).mean() 를 좀더 간결하게 아래처럼 쓸 수도 있습니다. 대상 데이터프레임을 제일 앞에 써주고, groupby()에 집계의 기준이 되는 key 변수들을 써주고, 제일 뒤에 집계하려는 연속형 변수이름을 써주었습니다. 



abalone.groupby(['sex', 'length_cat'])['whole_weight'].mean()

 

sex  length_cat  
F    length_long     1.261330
     length_short    0.589702
I    length_long     0.923215
     length_short    0.351234
M    length_long     1.255182
     length_short    0.538157
Name: whole_weight, dtype: float64




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

728x90
반응형
Posted by Rfriend
,