본문 바로가기

데이터분석 with 파이썬/pandas와 Numpy

1. <Pandas> Gruopby 그룹연산의 기초

그룹화시키기 GruopBy 기초
In [1]:
#본 실습내용은 출판사 O'REILLY의 Pyton for Data Analisys를 참고하여 만들었음을 말씀드립니다. 
from numpy.random import randn
import numpy as np
import os
import matplotlib.pyplot as plt
from pandas import Series, DataFrame
import pandas as pd

GroupBy mechanics

  1. 그룹 간 순회하기
  2. 칼럼 또는 칼럼의 일부만 선택하기
  3. 사전과 Series 에서 묶기
  4. 함수로 묶기
  5. 색인 단계로 묶기 그룹연산: 분리 - 적용 - 결합
  6. 객체 안의 데이터를 하나 이상의 색인을 기준으로 분리
  7. 함수를 각 그룹에 적용하여 새로운 값 생성
  8. 함수를 적용한 결과를 하나의 객체로 결합 그룹 색인의 형태 그룹으로 묶을 축과 같은 길이의 리스트나 배열 DataFrame의 칼럼 이름을 지칭하는 값 그룹으로 묶을 값과 그룹 이름에 대응하는 사전이나 Series 객체 축 색인 혹은 색인 내의 개별 이름에 대해 실행되는 함수
In [88]:
tips = pd.read_csv("tips.csv")
tips = tips.sample(10)  # tips 파일의 형태 확인.샘플로 10개를 추출!!
tips
Out[88]:
total_bill tip sex smoker day time size
219 30.14 3.09 Female Yes Sat Dinner 4
58 11.24 1.76 Male Yes Sat Dinner 2
171 15.81 3.16 Male Yes Sat Dinner 2
222 8.58 1.92 Male Yes Fri Lunch 1
93 16.32 4.30 Female Yes Fri Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
181 23.33 5.65 Male Yes Sun Dinner 2
5 25.29 4.71 Male No Sun Dinner 4
103 22.42 3.48 Female Yes Sat Dinner 2
29 19.65 3.00 Female No Sat Dinner 2
In [89]:
grouped = tips['total_bill'].groupby(tips["sex"]) # total bill을 sex기준으로 그룹화한다. 
In [90]:
grouped.mean()  # 그룹화된 객체마다의 평균을 구한다. # 각 성별의 평균 total bill
Out[90]:
sex
Female    22.624
Male      16.850
Name: total_bill, dtype: float64
In [91]:
means= tips['total_bill'].groupby([tips['sex'],tips['day']]).mean() 
# 그룹화의 기준을 '성별'과 '요일' 두개로 줄 수도 있다. GROOUPBY 안에 []리스트의 형태로 연달아 넣어준다.
means
Out[91]:
sex     day
Female  Fri    16.320
        Sat    24.070
        Sun    24.590
Male    Fri     8.580
        Sat    13.525
        Sun    24.310
Name: total_bill, dtype: float64
In [92]:
means.unstack()  #  다중색인의 중 제일 안쪽 인덱스가 컬럼으로 올라간 형태의 DataFrame으로 만든다. 
Out[92]:
day Fri Sat Sun
sex
Female 16.32 24.070 24.59
Male 8.58 13.525 24.31
In [93]:
randomNUM = np.random.randint(1,10,len(tips)) # tips와 길이가 같은 랜덤 어레이 생성
randomNUM
Out[93]:
array([4, 3, 2, 2, 9, 1, 9, 9, 5, 1])
In [94]:
tips.groupby(randomNUM).mean()  # 이렇게 길이가 같은 배열을 가지고 tips를 group화 할 수도 있다. 
#랜덤하게 생성된 각 번호로 group화 된 것을 확인 할 수 있다. 
Out[94]:
total_bill tip size
1 22.120000 3.305000 3.000000
2 12.195000 2.540000 1.500000
3 11.240000 1.760000 2.000000
4 30.140000 3.090000 4.000000
5 22.420000 3.480000 2.000000
9 21.646667 4.886667 2.666667

1. 그룹 간 순회하기

그룹 객체는 이터레이터 지원, 그룹이름과 데이터 묶음을 튜플로 반환

In [95]:
grouped  # 보기와 같이 구룹화된 객체들은 눈으로 바로 확인할 수 없습니다. 
         # 이래선 그룹화된 각 각의 그룹을 사용하기도 힘들곘죠. 이를 위해 그룹객체는 이터레이터를 지원합니다. 
Out[95]:
<pandas.core.groupby.SeriesGroupBy object at 0x078D7C70>
In [96]:
for name, group in grouped : # 현재 groued는 성별로 그룹화되어 있습니다.
    print(name)
    print(group)

    #그룹객체는 for문으로 들어갈 수 있는데 이때, 그룹화된 기준의 해당key와 해당 그룹을 반환해줍니다. 
Female
219    30.14
93     16.32
4      24.59
103    22.42
29     19.65
Name: total_bill, dtype: float64
Male
58     11.24
171    15.81
222     8.58
181    23.33
5      25.29
Name: total_bill, dtype: float64
In [97]:
for (key1, key2),group in tips.groupby(['sex','smoker']): #튜플형태의 다중키와  그룹객체 반환.
    print((key1,key2))   #각각의 그룹화된 키를 보여주고
    print(group.mean())  #  평균을 구할 수 있는 데이터들의 모든 평균값을 보여줍니다. 
('Female', 'No')
total_bill    22.120
tip            3.305
size           3.000
dtype: float64
('Female', 'Yes')
total_bill    22.960000
tip            3.623333
size           2.666667
dtype: float64
('Male', 'No')
total_bill    25.29
tip            4.71
size           4.00
dtype: float64
('Male', 'Yes')
total_bill    14.7400
tip            3.1225
size           1.7500
dtype: float64

그룹객체들을 사전형태로 만들어 각각 접근하기

In [98]:
eachGroup = dict(list(tips.groupby('day')))
eachGroup # 그룹과된 key에 따라 사전이 만들어지는데, 예를 들어 현재는 {Fri:해당 tips데이터} 식으로 
          # 사전에 저장된다. 
Out[98]:
{'Fri':      total_bill   tip     sex smoker  day    time  size
 222        8.58  1.92    Male    Yes  Fri   Lunch     1
 93        16.32  4.30  Female    Yes  Fri  Dinner     2,
 'Sat':      total_bill   tip     sex smoker  day    time  size
 219       30.14  3.09  Female    Yes  Sat  Dinner     4
 58        11.24  1.76    Male    Yes  Sat  Dinner     2
 171       15.81  3.16    Male    Yes  Sat  Dinner     2
 103       22.42  3.48  Female    Yes  Sat  Dinner     2
 29        19.65  3.00  Female     No  Sat  Dinner     2,
 'Sun':      total_bill   tip     sex smoker  day    time  size
 4         24.59  3.61  Female     No  Sun  Dinner     4
 181       23.33  5.65    Male    Yes  Sun  Dinner     2
 5         25.29  4.71    Male     No  Sun  Dinner     4}
In [99]:
eachGroup['Fri']  # 이렇게 내가 그룹화한 키로 사전에서 추출 가능하다! 
                  #  이로써 내가 각각의 그룹을 해당 key로 따로따로 접근할 수 있게 된다!
                  # 시각화할때 큰 도움이 될 것이다. 이를 몰라서 고생했던 기억이....
Out[99]:
total_bill tip sex smoker day time size
222 8.58 1.92 Male Yes Fri Lunch 1
93 16.32 4.30 Female Yes Fri Dinner 2

데이터 타입으로 그룹화 하기.

In [100]:
tips.dtypes
Out[100]:
total_bill    float64
tip           float64
sex            object
smoker         object
day            object
time           object
size            int64
dtype: object
In [101]:
groupedType = tips.groupby(tips.dtypes,axis=1)  # 데이터 타입을 기준으로, 타입은 열단위로 묶으므로 axis=1
dict(list(groupedType))
Out[101]:
{dtype('int64'):      size
 219     4
 58      2
 171     2
 222     1
 93      2
 4       4
 181     2
 5       4
 103     2
 29      2, dtype('float64'):      total_bill   tip
 219       30.14  3.09
 58        11.24  1.76
 171       15.81  3.16
 222        8.58  1.92
 93        16.32  4.30
 4         24.59  3.61
 181       23.33  5.65
 5         25.29  4.71
 103       22.42  3.48
 29        19.65  3.00, dtype('O'):         sex smoker  day    time
 219  Female    Yes  Sat  Dinner
 58     Male    Yes  Sat  Dinner
 171    Male    Yes  Sat  Dinner
 222    Male    Yes  Fri   Lunch
 93   Female    Yes  Fri  Dinner
 4    Female     No  Sun  Dinner
 181    Male    Yes  Sun  Dinner
 5      Male     No  Sun  Dinner
 103  Female    Yes  Sat  Dinner
 29   Female     No  Sat  Dinner}

2. 칼럼 또는 칼럼의 일부만 선택하기

DataFrame에서 만든 그룹 객체를 칼럼 이름이나, 칼럼이름이 담긴 배열로 색인하면 수집을 위해 해당 칼럼을 선택 groupby 메서드의 색인의 인자 값 단일 값으로 칼럼 이름 전달: SeriesGroupBy 객체 반환 : df.groupby(['key1', 'key2'])['data2'] 리스트나 배열 전달: DataFrameGroupBy 객체 반환 : df.groupby(['key1', 'key2'])[['data2']]

In [102]:
tips.groupby('day')[['tip']].mean()  # 그룹화 연산에서 해당 값을 추출할 때, [[]]리스트 안에 리스트를 넣어주면
                                     # 그 결과로 DATAFRAME을 반환해준다. 
Out[102]:
tip
day
Fri 3.110000
Sat 2.898000
Sun 4.656667
In [103]:
tips.groupby('day')['tip'].mean() # 그룹화 연산에서 해당 값을 추출할 때, [] 리스트만 넣어주면
                                  # 그 결과로 Series객체를 반환해준다. 
Out[103]:
day
Fri    3.110000
Sat    2.898000
Sun    4.656667
Name: tip, dtype: float64

3. 사전과 Series 에서 묶기

In [104]:
tips = tips.sample(10) # 데이터 사이즈를 좀 줄일게요. 10개만 샘플링을 합니다. 
tips
Out[104]:
total_bill tip sex smoker day time size
29 19.65 3.00 Female No Sat Dinner 2
5 25.29 4.71 Male No Sun Dinner 4
103 22.42 3.48 Female Yes Sat Dinner 2
222 8.58 1.92 Male Yes Fri Lunch 1
171 15.81 3.16 Male Yes Sat Dinner 2
219 30.14 3.09 Female Yes Sat Dinner 4
93 16.32 4.30 Female Yes Fri Dinner 2
181 23.33 5.65 Male Yes Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
58 11.24 1.76 Male Yes Sat Dinner 2

다양한 객체들로 groupby 연산을 할 수 있습니다. 지금까지는 tips의 컬럼으로 했지만 다른 타입으로도 해보겠습니다

In [105]:
# 각 칼럼을 나타낼 사전타입의 목록생성  #숫자형태의 칼럼들만 num이라는 값을 가지는 사전형태를 만들었습니다.
mapping = {'total_bill':'num','tip':'num','sex':'string','smoker':'string2','day':'string3',
           'time':'string4','size':'num'}
In [106]:
groupByDic = tips.groupby(mapping,axis=1)  # 컬럼 기준으로 묶이기 떄문에 axis=1
groupByDic.sum()  # 숫자로 된 칼럼들은 num이라는 칼럼으로 그룹화되어 합쳐진 것을 볼 수 있습니다. 
Out[106]:
num string string2 string3 string4
29 24.65 Female No Sat Dinner
5 34.00 Male No Sun Dinner
103 27.90 Female Yes Sat Dinner
222 11.50 Male Yes Fri Lunch
171 20.97 Male Yes Sat Dinner
219 37.23 Female Yes Sat Dinner
93 22.62 Female Yes Fri Dinner
181 30.98 Male Yes Sun Dinner
4 32.20 Female No Sun Dinner
58 15.00 Male Yes Sat Dinner
In [107]:
# 사전형태 뿐만 아니라 시리즈 객체로도 가능합니다. 
mapSeries = Series(mapping)
mapSeries
Out[107]:
day           string3
sex            string
size              num
smoker        string2
time          string4
tip               num
total_bill        num
dtype: object
In [108]:
groupBySeries=tips.groupby(mapSeries,axis=1)
groupBySeries.sum()  # 위와 같은 값이 나오는 것으 확인할 수 있죠. 
Out[108]:
num string string2 string3 string4
29 24.65 Female No Sat Dinner
5 34.00 Male No Sun Dinner
103 27.90 Female Yes Sat Dinner
222 11.50 Male Yes Fri Lunch
171 20.97 Male Yes Sat Dinner
219 37.23 Female Yes Sat Dinner
93 22.62 Female Yes Fri Dinner
181 30.98 Male Yes Sun Dinner
4 32.20 Female No Sun Dinner
58 15.00 Male Yes Sat Dinner

4. 함수로 묶기

그룹 색인으로 넘긴 함수는 색인 값 하나마다 한 번씩 호출되며, 반환 값은 그 그룹의 이름으로 사용 groupby 안에 어떤 칼럼이나, 사전, 시리즈를 넣는 것 말고 함수만을 넣어줄 수 도 있는데, 이는 각 인덱스에 대한 적용으로 그룹화 한다. 글로는 설명이 명확하지 않으니 예제를 보고 이해해보자.

In [109]:
groupedDay = tips.groupby('day')[['tip','total_bill']].mean()
groupedDay
Out[109]:
tip total_bill
day
Fri 3.110000 12.450000
Sat 2.898000 19.852000
Sun 4.656667 24.403333
In [110]:
groupedDay.groupby(len).sum()  #groupby 안에 내장함수를 써준다면 각 인덱스에 대해 적용한다. 
# 아래는 len 함수를 통해 각 인덱스의 길이로 묶어서 sum을 구한 결과이다. 
# Fri,Sat,Sun 글자가 3인 값들의 합, Thur 글자가 4인 값의 합이다. 
Out[110]:
tip total_bill
3 10.664667 56.705333

5. 색인 단계로 묶기

계층적으로 색인된 데이터 묶음은 축 색인의 단계 중 하나를 사용하여 그룹핑 : level 인자로 레벨 번호나 이름 전달

In [111]:
tips_multi = tips.groupby([tips['day'],tips['sex'],tips['smoker']])
tips_multi.mean().unstack()  # 지금의 결과값을 잘 확인해 놓자.
#이 다음에 멀티 칼럼과 인덱스에서 칼럼의 이름으로 그룹화하는 것을 해볼 것이다;.
Out[111]:
total_bill tip size
smoker No Yes No Yes No Yes
day sex
Fri Female NaN 16.320 NaN 4.300 NaN 2.0
Male NaN 8.580 NaN 1.920 NaN 1.0
Sat Female 19.65 26.280 3.00 3.285 2.0 3.0
Male NaN 13.525 NaN 2.460 NaN 2.0
Sun Female 24.59 NaN 3.61 NaN 4.0 NaN
Male 25.29 23.330 4.71 5.650 4.0 2.0
In [112]:
tips_multi.mean().unstack().groupby(level = 'smoker',axis= 1).count()
# 멀티 칼럼에서 smoker라는 칼럼을 기준으로, axis = 1 로 그룹화하여 count 한다. 
Out[112]:
smoker No Yes
day sex
Fri Female 0 3
Male 0 3
Sat Female 3 3
Male 0 3
Sun Female 3 0
Male 3 3