카테고리 없음

3. <Pandas> 그룹연산과 변형 transform()/apply()/cut()/qcut()/pivot()/crosstab()

Mr.jjong 2017. 4. 24. 16:58
그룹별 연간과 변형
In [2]:
#본 실습내용은 출판사 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
In [46]:
tips = pd.read_csv('tips.csv')
tips = tips.sample(10)
tips
Out[46]:
total_bill tip sex smoker day time size
145 8.35 1.50 Female No Thur Lunch 2
59 48.27 6.73 Male No Sat Dinner 4
109 14.31 4.00 Female Yes Sat Dinner 2
123 15.95 2.00 Male No Thur Lunch 2
234 15.53 3.00 Male Yes Sat Dinner 2
44 30.40 5.60 Male No Sun Dinner 4
25 17.81 2.34 Male No Sat Dinner 4
134 18.26 3.25 Female No Thur Lunch 2
33 20.69 2.45 Female No Sat Dinner 4
91 22.49 3.50 Male No Fri Dinner 2

그룹별 연산과 변형

  • transform() 인자: 스칼라 값이나 같은 크기를 가지는 배열을 반환하는 함수
  • apply() 인자: pandas 객체나 스칼라 값을 반환하는 함수
  • apply: 분리 - 적용 - 병합
  • 그룹 색인 생략하기
  • 변위치 분석과 버킷 분석
  • 그룹에 국한된 값으로 누락된 값 채우기

add_prefix()

In [70]:
tip_means = tips.groupby('sex').mean().add_prefix('mean_')  
tip_means
Out[70]:
mean_total_bill mean_tip mean_size
sex
Female 15.4025 2.800000 2.5
Male 25.0750 3.861667 3.0

transform() 은 들어온 메서드에 대해 각 원소를 살리고 그 안에 연산결과를 채운다.

In [71]:
tips.groupby('day').transform(np.mean) 
Out[71]:
total_bill tip size
145 14.186667 2.250 2.0
59 23.322000 3.704 3.2
109 23.322000 3.704 3.2
123 14.186667 2.250 2.0
234 23.322000 3.704 3.2
44 30.400000 5.600 4.0
25 23.322000 3.704 3.2
134 14.186667 2.250 2.0
33 23.322000 3.704 3.2
91 22.490000 3.500 2.0

apply() 함수는 그룹별로 행당 한줄의 정리된 결과값으로 정리되어 출력된다.

In [72]:
tips.groupby('day').apply(np.mean)
Out[72]:
total_bill tip size
day
Fri 22.490000 3.500 2.0
Sat 23.322000 3.704 3.2
Sun 30.400000 5.600 4.0
Thur 14.186667 2.250 2.0

1. apply: 분리 - 적용 - 병합

In [73]:
tips['pct']= tips['tip']/tips['total_bill']*100  # 팁 비율에 대해 추가. 
tips
Out[73]:
total_bill tip sex smoker day time size pct
145 8.35 1.50 Female No Thur Lunch 2 17.964072
59 48.27 6.73 Male No Sat Dinner 4 13.942407
109 14.31 4.00 Female Yes Sat Dinner 2 27.952481
123 15.95 2.00 Male No Thur Lunch 2 12.539185
234 15.53 3.00 Male Yes Sat Dinner 2 19.317450
44 30.40 5.60 Male No Sun Dinner 4 18.421053
25 17.81 2.34 Male No Sat Dinner 4 13.138686
134 18.26 3.25 Female No Thur Lunch 2 17.798467
33 20.69 2.45 Female No Sat Dinner 4 11.841469
91 22.49 3.50 Male No Fri Dinner 2 15.562472
In [74]:
def top (df,n=5,column = 'pct'):  #함수 설명 : 인자는 데이터프레임(df),갯수(n),컬럼이름(column)을 받는다.
    return df.sort_values(by=column)[-n:] #그중 해당칼럼의 상위 n개의 로우를 반환한다. 
top(tips,n=6)
Out[74]:
total_bill tip sex smoker day time size pct
91 22.49 3.50 Male No Fri Dinner 2 15.562472
134 18.26 3.25 Female No Thur Lunch 2 17.798467
145 8.35 1.50 Female No Thur Lunch 2 17.964072
44 30.40 5.60 Male No Sun Dinner 4 18.421053
234 15.53 3.00 Male Yes Sat Dinner 2 19.317450
109 14.31 4.00 Female Yes Sat Dinner 2 27.952481
In [75]:
tips.groupby('smoker').apply(top) #top 함수에 아무런 인자(parameter)를 주지 않았음으로 pct에서 상위5개 추출
Out[75]:
total_bill tip sex smoker day time size pct
smoker
No 59 48.27 6.73 Male No Sat Dinner 4 13.942407
91 22.49 3.50 Male No Fri Dinner 2 15.562472
134 18.26 3.25 Female No Thur Lunch 2 17.798467
145 8.35 1.50 Female No Thur Lunch 2 17.964072
44 30.40 5.60 Male No Sun Dinner 4 18.421053
Yes 234 15.53 3.00 Male Yes Sat Dinner 2 19.317450
109 14.31 4.00 Female Yes Sat Dinner 2 27.952481
In [76]:
tips.groupby('smoker').apply(top,n=1,column='total_bill') # total_bill에서 상위 1개씩 뽑아내기 
Out[76]:
total_bill tip sex smoker day time size pct
smoker
No 59 48.27 6.73 Male No Sat Dinner 4 13.942407
Yes 234 15.53 3.00 Male Yes Sat Dinner 2 19.317450

그룹함수 적용의 색인 없애기

groupby+apply 연산의 결과값을 보면 색인과 결과값안의 데이터가 중복되는 것을 볼 수 있다. 이때를 위해 있는 함수인자가 group_keys라고 있다.

In [77]:
tips.groupby('smoker',group_keys=False).apply(top)
Out[77]:
total_bill tip sex smoker day time size pct
59 48.27 6.73 Male No Sat Dinner 4 13.942407
91 22.49 3.50 Male No Fri Dinner 2 15.562472
134 18.26 3.25 Female No Thur Lunch 2 17.798467
145 8.35 1.50 Female No Thur Lunch 2 17.964072
44 30.40 5.60 Male No Sun Dinner 4 18.421053
234 15.53 3.00 Male Yes Sat Dinner 2 19.317450
109 14.31 4.00 Female Yes Sat Dinner 2 27.952481
In [78]:
tips.groupby('smoker',group_keys=False).apply(top).describe()
Out[78]:
total_bill tip size pct
count 7.000000 7.000000 7.000000 7.000000
mean 22.515714 3.940000 2.571429 18.708343
std 13.293848 1.734464 0.975900 4.470274
min 8.350000 1.500000 2.000000 13.942407
25% 14.920000 3.125000 2.000000 16.680469
50% 18.260000 3.500000 2.000000 17.964072
75% 26.445000 4.800000 3.000000 18.869251
max 48.270000 6.730000 4.000000 27.952481

2. 변위치 분석과 버킷 분석

cut(), qcut() 메서드 이용 선택한 크기만큼 혹은 표본 변위치에 따라 데이터 분리 groupby 와 조합하여 데이터 묶음에 대한 변위치 분석이나 버킷 분석

cut() / qcut()계열이 매서드는 기본적으로 설정된 칼럼에 대한 sorting(정렬)을 진행한다.

cut() : 지정된 칼럼의 최솟값과 최댓값을 등분위로 나누는 메서드

In [79]:
factor_cut = pd.cut(tips['tip'],4)
factor_cut   # 4등분된 크기로 각 로우마다 카테고리화된 값들이 나열되어 있다. 
Out[79]:
145    (1.495, 2.808]
59      (5.423, 6.73]
109    (2.808, 4.115]
123    (1.495, 2.808]
234    (2.808, 4.115]
44      (5.423, 6.73]
25     (1.495, 2.808]
134    (2.808, 4.115]
33     (1.495, 2.808]
91     (2.808, 4.115]
Name: tip, dtype: category
Categories (4, object): [(1.495, 2.808] < (2.808, 4.115] < (4.115, 5.423] < (5.423, 6.73]]
In [80]:
tips.groupby(factor_cut).agg(['size','mean'])  # 지난 포스트에서 배웠듯이, groupby에 로우의 수와 같은 array를 넣어주면
                             # 행당 키에 맞게 그룹화시키고 이에 대한 size, mean값을 구한 것이다. 
Out[80]:
total_bill tip size pct
size mean size mean size mean size mean
tip
(1.495, 2.808] 4 15.7000 4 2.0725 4 3.0 4 13.870853
(2.808, 4.115] 4 17.6475 4 3.4375 4 2.0 4 20.157717
(4.115, 5.423] 0 NaN 0 NaN 0 NaN 0 NaN
(5.423, 6.73] 2 39.3350 2 6.1650 2 4.0 2 16.181730

위 결과값을 보면 데이터 크기들 4등분 하였음으로 size가 각 그룹마다 다른 것을 볼 수 있다.

그렇다면 데이터 갮을 4등분하는 것이 아니라 데이터의 갯수를 4등분 할 수 는 없을까? 이를 위해서 qcou()이라는 메서드를 사용할 수 있다.

qcut() : 주어진 데이터의 갯수를 n등분하는 메서드

In [81]:
tips
Out[81]:
total_bill tip sex smoker day time size pct
145 8.35 1.50 Female No Thur Lunch 2 17.964072
59 48.27 6.73 Male No Sat Dinner 4 13.942407
109 14.31 4.00 Female Yes Sat Dinner 2 27.952481
123 15.95 2.00 Male No Thur Lunch 2 12.539185
234 15.53 3.00 Male Yes Sat Dinner 2 19.317450
44 30.40 5.60 Male No Sun Dinner 4 18.421053
25 17.81 2.34 Male No Sat Dinner 4 13.138686
134 18.26 3.25 Female No Thur Lunch 2 17.798467
33 20.69 2.45 Female No Sat Dinner 4 11.841469
91 22.49 3.50 Male No Fri Dinner 2 15.562472
In [82]:
factor_qcut = pd.qcut(tips.total_bill, 5,labels=False)
factor_qcut.value_counts()
factor_qcut
Out[82]:
145    0
59     4
109    0
123    1
234    1
44     4
25     2
134    2
33     3
91     3
Name: total_bill, dtype: int32
In [83]:
factor_qcut = pd.qcut(tips.tip, 5,labels=False)
factor_qcut
Out[83]:
145    0
59     4
109    3
123    0
234    2
44     4
25     1
134    2
33     1
91     3
Name: tip, dtype: int32
In [84]:
tips.groupby(factor_qcut).agg(['size','mean']) 
Out[84]:
total_bill tip size pct
size mean size mean size mean size mean
tip
0 2 12.150 2 1.750 2 2 2 15.251628
1 2 19.250 2 2.395 2 4 2 12.490078
2 2 16.895 2 3.125 2 2 2 18.557958
3 2 18.400 2 3.750 2 2 2 21.757476
4 2 39.335 2 6.165 2 4 2 16.181730

그룹에 국한된 값으로 누락된 값 채우기

fillna() 삘나를 사용하여 각 그룹별로 다른 값을 채울 수 있다.

In [85]:
S= Series(np.random.randn(6))
S[::2]=np.nan
S
Out[85]:
0         NaN
1   -0.363890
2         NaN
3   -0.546019
4         NaN
5   -1.057021
dtype: float64
In [86]:
S.fillna(S.mean())   # Nan 이었던 부분이 S의 평균으로 채워진 것을 볼 수 있다. 
Out[86]:
0   -0.655643
1   -0.363890
2   -0.655643
3   -0.546019
4   -0.655643
5   -1.057021
dtype: float64
In [87]:
#그룹별 NaN 채우기 예제
states = ['Ohio', 'New York', 'Vermont', 'Florida',
          'Oregon', 'Nevada', 'California', 'Idaho']

group_key = ['East'] * 4 + ['West'] * 4

data = Series(np.random.randn(8), index=states)
data[['Vermont', 'Nevada', 'Idaho']] = np.nan
data
Out[87]:
Ohio          0.047260
New York      0.221294
Vermont            NaN
Florida       0.435544
Oregon        0.831985
Nevada             NaN
California   -1.552943
Idaho              NaN
dtype: float64
In [88]:
data.groupby(group_key).mean()
Out[88]:
East    0.234700
West   -0.360479
dtype: float64
In [89]:
fill_mean = lambda g : g.fillna(g.mean())
In [90]:
data.groupby(group_key).apply(fill_mean)
Out[90]:
Ohio          0.047260
New York      0.221294
Vermont       0.234700
Florida       0.435544
Oregon        0.831985
Nevada       -0.360479
California   -1.552943
Idaho        -0.360479
dtype: float64
In [91]:
# 그룹에 따라 정의된 값 채우기
# group의 name 속성 이용
# group_key = ['East'] * 4 + ['West'] * 4

fill_values = {'East': 0.5, 'West': -1}   #해당 컬럼이름의 사전형태로 채울 값을 설정해 놓는다. 
fill_func = lambda g: g.fillna(fill_values[g.name])# g.name!!!!!이거 중요!! 사전에 g.name을 넣어 값을 갖고온다

data.groupby(group_key).apply(fill_func)
Out[91]:
Ohio          0.047260
New York      0.221294
Vermont       0.500000
Florida       0.435544
Oregon        0.831985
Nevada       -1.000000
California   -1.552943
Idaho        -1.000000
dtype: float64

피벗테이블과 교차일람표

데이터 요약화 도구 데이터를 하나 이상의 키로 수집해서 어떤 키는 로우에, 어떤 키는 칼럼에 나열해서 데이터를 정렬 DataFrame에 pivot_table() 또는 pandas.pivot_table pivot_table() margins=True는 부분합 추가 기능 제공 (groupby) pivot_table 옵션 [표9-2] 379p

피벗테이블 : 어찌보면 groupby의 연산과 동일해 보일 수 있으나 더 간단하고 간략하게 groupby 연산을 정리할 수 있다는 장점을 갖는다.

교차일람표 : 특수하게 '빈도수'를 세어야 할 때 쓰인다.

1> 교차일람표

In [92]:
tips
Out[92]:
total_bill tip sex smoker day time size pct
145 8.35 1.50 Female No Thur Lunch 2 17.964072
59 48.27 6.73 Male No Sat Dinner 4 13.942407
109 14.31 4.00 Female Yes Sat Dinner 2 27.952481
123 15.95 2.00 Male No Thur Lunch 2 12.539185
234 15.53 3.00 Male Yes Sat Dinner 2 19.317450
44 30.40 5.60 Male No Sun Dinner 4 18.421053
25 17.81 2.34 Male No Sat Dinner 4 13.138686
134 18.26 3.25 Female No Thur Lunch 2 17.798467
33 20.69 2.45 Female No Sat Dinner 4 11.841469
91 22.49 3.50 Male No Fri Dinner 2 15.562472
In [93]:
tips.pivot_table(index=['sex','smoker'])  ## 기본적으로 pivot_table은 평균을 계산한다. 
Out[93]:
pct size tip total_bill
sex smoker
Female No 15.868003 2.666667 2.400 15.766667
Yes 27.952481 2.000000 4.000 14.310000
Male No 14.720761 3.200000 4.034 26.984000
Yes 19.317450 2.000000 3.000 15.530000
In [94]:
tips.pivot_table(['tip','size'],index=['sex','smoker'],columns='time') 
# [tip,size] 에 대해서만 인덱스를 sex와 smoker로 column은 time으로 평균을 구한다. 
Out[94]:
tip size
time Dinner Lunch Dinner Lunch
sex smoker
Female No 2.4500 2.375 4.0 2.0
Yes 4.0000 NaN 2.0 NaN
Male No 4.5425 2.000 3.5 2.0
Yes 3.0000 NaN 2.0 NaN
In [95]:
tips.pivot_table(['tip','size'],index=['sex','smoker'],columns='time', margins=True) 
# margine = True 로 놓으면 단일 행과 열에 대한 total 값을 붙여준다. 
Out[95]:
tip size
time Dinner Lunch All Dinner Lunch All
sex smoker
Female No 2.450000 2.375 2.400 4.000000 2.0 2.666667
Yes 4.000000 NaN 4.000 2.000000 NaN 2.000000
Male No 4.542500 2.000 4.034 3.500000 2.0 3.200000
Yes 3.000000 NaN 3.000 2.000000 NaN 2.000000
All 3.945714 2.250 3.437 3.142857 2.0 2.800000
In [96]:
tips.pivot_table(['tip','size'],index=['sex','smoker'],columns='time', margins=True, fill_value=0) 
# NaN값을 지정된 값으로 채워줄 수 있다. 
Out[96]:
tip size
time Dinner Lunch All Dinner Lunch All
sex smoker
Female No 2.450000 2.375 2.400 4.000000 2.0 2.666667
Yes 4.000000 0.000 4.000 2.000000 0.0 2.000000
Male No 4.542500 2.000 4.034 3.500000 2.0 3.200000
Yes 3.000000 0.000 3.000 2.000000 0.0 2.000000
All 3.945714 2.250 3.437 3.142857 2.0 2.800000
In [97]:
tips.pivot_table(['tip','size'],index=['sex','smoker'],columns='time', margins=True, fill_value=0,aggfunc='sum')
Out[97]:
tip size
time Dinner Lunch All Dinner Lunch All
sex smoker
Female No 2.45 4.75 7.20 4.0 4.0 8.0
Yes 4.00 0.00 4.00 2.0 0.0 2.0
Male No 18.17 2.00 20.17 14.0 2.0 16.0
Yes 3.00 0.00 3.00 2.0 0.0 2.0
All 27.62 6.75 34.37 22.0 6.0 28.0

2.교차일람표 crosstab

그룹 빈도 계산하기 위한 피벗 테이블의 특수한 경우 pandas의 crosstab() 인자: 배열이나 Series, 배열의 리스트

In [99]:
pd.crosstab([tips.time,tips.day],tips.smoker, margins=True)
Out[99]:
smoker No Yes All
time day
Dinner Fri 1 0 1
Sat 3 2 5
Sun 1 0 1
Lunch Thur 3 0 3
All 8 2 10
In [ ]: