본문 바로가기
개발일지/Pandas

pandas 판다스 기초 19 groupby, relabeling, aggregation, transform, apply, stack

by 다니엘의 개발 이야기 2022. 8. 12.
320x100
# Pandas GroupBy Operations

## Understanding GroupBy objects

import pandas as pd

titanic = pd.read_csv('titanic.csv')
titanic.head()

'''
    survived	pclass	sex	age	sibsp	parch	fare	embarked	deck
0	0	3	male	22.0	1	0	7.2500	S	NaN
1	1	1	female	38.0	1	0	71.2833	C	C
2	1	3	female	26.0	0	0	7.9250	S	NaN
3	1	1	female	35.0	1	0	53.1000	S	C
4	0	3	male	35.0	0	0	8.0500	S	NaN
'''
titanic.info()

'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 9 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   survived  891 non-null    int64  
 1   pclass    891 non-null    int64  
 2   sex       891 non-null    object 
 3   age       714 non-null    float64
 4   sibsp     891 non-null    int64  
 5   parch     891 non-null    int64  
 6   fare      891 non-null    float64
 7   embarked  889 non-null    object 
 8   deck      203 non-null    object 
dtypes: float64(2), int64(4), object(3)
memory usage: 62.8+ KB
'''
# titanic 변수에 대해서
# 행 rows를 처음부터 9번까지 슬라이스 해줘라
# 열 columns를 2,3번에 대해서 출력해줘라.

titanic.iloc[:10, [2,3]]

'''

    sex	age
0	male	22.0
1	female	38.0
2	female	26.0
3	female	35.0
4	male	35.0
5	male	NaN
6	male	54.0
7	male	2.0
8	female	27.0
9	female	14.0
'''
titanic_slice = titanic.iloc[:10, [2,3]]
titanic_slice.groupby('sex')

# <pandas.core.groupby.generic.DataFrameGroupBy object at 0x7f80c262cd60>
gbo = titanic_slice.groupby('sex')
type(gbo)

# pandas.core.groupby.generic.DataFrameGroupBy
# sex를 기준으로 그룹화 된 것들이 각각 인덱스 번호가 어떻게 되는지 표현해주는 메소드

gbo.groups

# {'female': [1, 2, 3, 8, 9], 'male': [0, 4, 5, 6, 7]}
l = list(gbo)

'''
[('female',
        sex   age
  1  female  38.0
  2  female  26.0
  3  female  35.0
  8  female  27.0
  9  female  14.0),
 ('male',
      sex   age
  0  male  22.0
  4  male  35.0
  5  male   NaN
  6  male  54.0
  7  male   2.0)]
'''
len(l)
# 2
l[0]

'''
('female',
       sex   age
 1  female  38.0
 2  female  26.0
 3  female  35.0
 8  female  27.0
 9  female  14.0)
'''
l[1]

'''
('male',
     sex   age
 0  male  22.0
 4  male  35.0
 5  male   NaN
 6  male  54.0
 7  male   2.0)
'''
titanic_slice.loc[titanic_slice.sex == 'female']

'''

    sex	age
1	female	38.0
2	female	26.0
3	female	35.0
8	female	27.0
9	female	14.0
'''
titanic.loc[titanic.sex == 'female'].reset_index()
titanic_slice_f = titanic_slice.loc[titanic_slice.sex == 'female']
titanic_slice_f.equals(l[0][1])

# True
for element in gbo:
    print(element[1])


## Splitting with many Keys

import pandas as pd
summer = pd.read_csv('summer.csv')
summer.head()

'''

    Year	City	Sport	Discipline	Athlete	Country	Gender	Event	Medal
0	1896	Athens	Aquatics	Swimming	HAJOS, Alfred	HUN	Men	100M Freestyle	Gold
1	1896	Athens	Aquatics	Swimming	HERSCHMANN, Otto	AUT	Men	100M Freestyle	Silver
2	1896	Athens	Aquatics	Swimming	DRIVAS, Dimitrios	GRE	Men	100M Freestyle For Sailors	Bronze
3	1896	Athens	Aquatics	Swimming	MALOKINIS, Ioannis	GRE	Men	100M Freestyle For Sailors	Gold
4	1896	Athens	Aquatics	Swimming	CHASAPIS, Spiridon	GRE	Men	100M Freestyle For Sailors	Silver
'''
summer.Country.nunique()

# 147

# nunique 정확히 무슨뜻이였더라?
# summer는 기본적으로 31165개의 행으로 이루어져있는 변수다.
# 그 중에서 Country 컬럼에 귀속된 수 많은 값 중에서
# 중복없이 고유한 값인 경우의 수가 147이라는 말 같다.
summer.info()

'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 31165 entries, 0 to 31164
Data columns (total 9 columns):
 #   Column      Non-Null Count  Dtype 
---  ------      --------------  ----- 
 0   Year        31165 non-null  int64 
 1   City        31165 non-null  object
 2   Sport       31165 non-null  object
 3   Discipline  31165 non-null  object
 4   Athlete     31165 non-null  object
 5   Country     31161 non-null  object
 6   Gender      31165 non-null  object
 7   Event       31165 non-null  object
 8   Medal       31165 non-null  object
dtypes: int64(1), object(8)
memory usage: 2.1+ MB
'''
split1 = summer.groupby('Country')
l = list(split1)
len(l)
# 147
# 국가명

l[1][0]
# 'AHO'
# 해당 국가에 귀속된 리스트
# 첫번째 리스트에 들어갈 숫자는 그 순서의 국가를 의미하고
# 두번째 리스트에 들어갈 숫자로는 0은 국가명
# 1은 해당 국가에 귀속된 인원들의 정보를 의미한다.

l[1][1]

'''
    Year	City	Sport	Discipline	Athlete	Country	Gender	Event	Medal
19323	1988	Seoul	Sailing	Sailing	BOERSMA, Jan D.	AHO	Men	Board (Division Ii)	Silver
'''
# 2개의 컬럼으로 그룹이라..

split2 = summer.groupby(by = ['Country', 'Gender'])
l2 = list(split2)

len(l2)

# 236
# 원리는 같다.
# 첫번째 리스트에 들어간 100은 100번 행 인덱스의 순서를 의미하고
# 두번째 리스트에 들어간 0은 groupby로 잡아준 Country 컬럼의 이름과 Gender 컬럼의 이름이 출력 된다.

l2[100][0]
# 두번째 컬럼 값이 1이면 귀속된 값이 나오고.
l2[100][1]

'''
    Year	City	Sport	Discipline	Athlete	Country	Gender	Event	Medal
21081	1992	Barcelona	Shooting	Shooting	PLETIKOSIC, Stevan	IOP	Men	50M Rifle Prone (60 Shots)	Bronze
'''


## split-apply-combine explained

import pandas as pd
titanic = pd.read_csv('titanic.csv')

titanic_slice = titanic.iloc[:10, [2,3]]

list(titanic_slice.groupby('sex'))[0][1]

'''

    sex	age
1	female	38.0
2	female	26.0
3	female	35.0
8	female	27.0
9	female	14.0
'''
list(titanic_slice.groupby('sex'))[1][1]

'''

	sex	age
0	male	22.0
4	male	35.0
5	male	NaN
6	male	54.0
7	male	2.0
'''
titanic.groupby('sex').sum()

'''
	survived	pclass	age	sibsp	parch	fare
sex						
female	233	678	7286.00	218	204	13966.6628
male	109	1379	13919.17	248	136	14727.2865
'''
# 위의 체인 메서드? 와 관련하여 한번 더 나아가자면 이렇게도 사용이 가능하다.
titanic.groupby('sex').survived.sum()

'''
sex
female    233
male      109
Name: survived, dtype: int64
'''
titanic.groupby('sex')[['fare', 'age']].max()

'''
	fare	age
sex		
female	512.3292	63.0
male	512.3292	80.0
'''
new_df = titanic.groupby('sex').mean()

'''

    survived	pclass	age	sibsp	parch	fare
sex						
female	0.742038	2.159236	27.915709	0.694268	0.649682	44.479818
male	0.188908	2.389948	30.726645	0.429809	0.235702	25.523893
'''
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('seaborn')
# subplots를 True로 했다는건, 각 그래프에 대해서 제목을 표시해주겠다. 라는 뜻이다.

new_df.plot(kind = 'bar', subplots = True, figsize = (8,15), fontsize = 13)
plt.show()



## split-apply-combine applied

import pandas as pd
summer = pd.read_csv('summer.csv')
# 종류에 상관없이 Country 기준으로 분류한 후에 Medal의 갯수를 세어주기
summer.groupby('Country').Medal.count()

'''
Country
AFG      2
AHO      1
ALG     15
ANZ     29
ARG    259
      ... 
VIE      2
YUG    435
ZAM      2
ZIM     23
ZZX     48
Name: Medal, Length: 147, dtype: int64
'''
medals_per_country = summer.groupby('Country').Medal.count()
medals_per_country

'''
Country
AFG      2
AHO      1
ALG     15
ANZ     29
ARG    259
      ... 
VIE      2
YUG    435
ZAM      2
ZIM     23
ZZX     48
Name: Medal, Length: 147, dtype: int64
'''
# 가장 큰 순서대로 20개 나열 하는 방법
# rank와 같은 기능

medals_per_country = summer.groupby('Country').Medal.count().nlargest(n = 20)
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('seaborn')
medals_per_country.plot(kind = 'bar', figsize = (14,8), fontsize = 14, rot = 45)
plt.xlabel('Country', fontsize = 13)
plt.ylabel('No. of Medals', fontsize = 13)
plt.title('Summer Olympic Games (Total Medals per Country)', fontsize = 16)
plt.show()

titanic = pd.read_csv('titanic.csv')
titanic.describe()

'''
	survived	pclass	age	sibsp	parch	fare
count	891.000000	891.000000	714.000000	891.000000	891.000000	891.000000
mean	0.383838	2.308642	29.699118	0.523008	0.381594	32.204208
std	0.486592	0.836071	14.526497	1.102743	0.806057	49.693429
min	0.000000	1.000000	0.420000	0.000000	0.000000	0.000000
25%	0.000000	2.000000	20.125000	0.000000	0.000000	7.910400
50%	0.000000	3.000000	28.000000	0.000000	0.000000	14.454200
75%	1.000000	3.000000	38.000000	1.000000	0.000000	31.000000
max	1.000000	3.000000	80.000000	8.000000	6.000000	512.329200
'''
# titanic의 운임료 평균
titanic.fare.mean()

# 32.2042079685746
# pclass 그룹별로 분할해서 정리한 후에
# 운임료의 평균

titanic.groupby('pclass').fare.mean()

'''
pclass
1    84.154687
2    20.662183
3    13.675550
Name: fare, dtype: float64
'''
# 생존자 수
titanic.survived.sum()

# 342

# 왜 이게 가능하냐면, 사망자는 0, 생존자는 1이기 때문이다.
titanic.groupby('sex').survived.mean()

'''
sex
female    0.742038
male      0.188908
Name: survived, dtype: float64
'''
# 생존률
titanic.groupby('pclass').survived.mean()

'''
pclass
1    0.629630
2    0.472826
3    0.242363
Name: survived, dtype: float64
'''
# 나이에 따른 아이를 구별하기 위한 수식이다.
# 왜냐하면 따로 구별이 없기 때문이다.
titanic['ad_chi'] = 'adult'
# 나이가 18 미만일 경우, ad_chi의 필드값을 child로 오버라이드 해준다.

titanic.loc[titanic.age < 18, 'ad_chi'] = 'child'
titanic.head()

'''

    survived	pclass	sex	age	sibsp	parch	fare	embarked	deck	ad_chi
0	0	3	male	22.0	1	0	7.2500	S	NaN	adult
1	1	1	female	38.0	1	0	71.2833	C	C	adult
2	1	3	female	26.0	0	0	7.9250	S	NaN	adult
3	1	1	female	35.0	1	0	53.1000	S	C	adult
4	0	3	male	35.0	0	0	8.0500	S	NaN	adult
'''
titanic.ad_chi.value_counts()

'''
adult    778
child    113
Name: ad_chi, dtype: int64
'''
# mean의 정확한 개념은, '구성비율'인것 같다.
titanic.groupby('ad_chi').survived.mean()

'''
ad_chi
adult    0.361183
child    0.539823
Name: survived, dtype: float64
'''
titanic.groupby(['sex', 'ad_chi']).survived.count()

'''
sex     ad_chi
female  adult     259
        child      55
male    adult     519
        child      58
Name: survived, dtype: int64
'''
titanic.groupby(['sex', 'ad_chi']).survived.mean().sort_values(ascending = False)

'''
sex     ad_chi
female  adult     0.752896
        child     0.690909
male    child     0.396552
        adult     0.165703
Name: survived, dtype: float64
'''
w_and_c_first = titanic.groupby(['sex', 'ad_chi']).survived.mean().sort_values(ascending = False)
w_and_c_first.plot(kind = 'bar', figsize = (14,8), fontsize = 14, rot = 360)
plt.xlabel('Groups', fontsize = 13)
plt.ylabel('Survival Rate', fontsize = 13)
plt.title('Titanic Survival Rate by Sex/Age-Groups', fontsize = 16)
plt.show()

titanic.groupby('sex')[['survived', 'pclass', 'age', 'fare']].agg(['sum', 'mean'])

'''

    survived	pclass	age	fare
    sum	mean	sum	mean	sum	mean	sum	mean
sex								
female	233	0.742038	678	2.159236	7286.00	27.915709	13966.6628	44.479818
male	109	0.188908	1379	2.389948	13919.17	30.726645	14727.2865	25.523893
'''


## Advanced Aggregation with agg()

import pandas as pd

titanic = pd.read_csv('titanic.csv', usecols = ['survived', 'pclass', 'sex', 'age', 'fare'])
titanic.head()

'''
	survived	pclass	sex	age	fare
0	0	3	male	22.0	7.2500
1	1	1	female	38.0	71.2833
2	1	3	female	26.0	7.9250
3	1	1	female	35.0	53.1000
4	0	3	male	35.0	8.0500
'''
# titanic.groupby('sex')
# 단순히 groupby 만 해주면
# <pandas.core.groupby.generic.DataFrameGroupBy object at 0x7fab88bc0dc0>
# 이렇게 출력 된다.
# 따라서, 활용을 위해서는 변수지정을 해서 변수로 활용해주거나,
titanic.groupby('sex').mean()

'''
    survived	pclass	age	fare
sex				
female	0.742038	2.159236	27.915709	44.479818
male	0.188908	2.389948	30.726645	25.523893
'''

# 이렇게 활용해 준다.
titanic.groupby('sex').sum()

'''
	survived	pclass	age	fare
sex				
female	233	678	7286.00	13966.6628
male	109	1379	13919.17	14727.2865
'''
# groupby 다중 선택
titanic.groupby('sex').agg(['mean', 'sum', 'min', 'max'])

'''

    survived	pclass	age	fare
    mean	sum	min	max	mean	sum	min	max	mean	sum	min	max	mean	sum	min	max
sex																
female	0.742038	233	0	1	2.159236	678	1	3	27.915709	7286.00	0.75	63.0	44.479818	13966.6628	6.75	512.3292
male	0.188908	109	0	1	2.389948	1379	1	3	30.726645	13919.17	0.42	80.0	25.523893	14727.2865	0.00	512.3292
'''
# 특정 컬럼을
# 딕셔너리 처리해서
# 다중 조건으로 groupby 해주기
titanic.groupby('sex').agg({'survived': ['sum', 'mean'], 'pclass': 'mean', 'age':['mean', 'median'], 'fare':'max'})


## Groupby Aggregation with Relabeling (new in Version 0.25)

import pandas as pd
titanic = pd.read_csv('titanic.csv', usecols = ['survived', 'pclass', 'sex', 'age', 'fare'])
titanic.head()

'''

    survived	pclass	sex	age	fare
0	0	3	male	22.0	7.2500
1	1	1	female	38.0	71.2833
2	1	3	female	26.0	7.9250
3	1	1	female	35.0	53.1000
4	0	3	male	35.0	8.0500
'''
titanic.groupby('sex').survived.mean()

'''
sex
female    0.742038
male      0.188908
Name: survived, dtype: float64
'''
titanic.groupby('sex').agg(survival_rate = ('survived', 'mean'))

'''
    survival_rate
sex	
female	0.742038
male	0.188908
'''

# 이 부분이 흥미롭다.
# survival_rate 라는 함수가 agg에 내장 되어있는건가?
# 비율을 구해주네.
titanic.groupby('sex').agg({'survived':['sum', 'mean'], 'age':'mean'})

'''
    survived	age
    sum	mean	mean
sex			
female	233	0.742038	27.915709
male	109	0.188908	30.726645
'''
titanic.groupby('sex').agg(survived_total = ('survived', 'sum'),
                          survival_rate = ('survived', 'mean'), mean_age = ('age', 'mean'))

'''
	survived_total	survival_rate	mean_age
sex			
female	233	0.742038	27.915709
male	109	0.188908	30.726645
'''

# 각 함수의 이름이 직관적이긴 하나, 정말 저런 함수들이 있다고?
# 의문이 들게끔 하는 이름이다.
# 하지만 결과적으로는 너무 잘 출력 된다.

# 저런 이름의 함수들이 있는게 아니라. 기본 원리는 a = 1+2라고 할때, a는 3이 되는 것과 마찬가지다.
# total에 있어서는 survived컬럼의 sum을 해준 숫자이므로, 자동으로 모든 수의 합. 즉, total이 나오며
# mean에 있어서는 구성비율을 나타내게 된다.


## Transformation with transform()

import pandas as pd
titanic = pd.read_csv('titanic.csv')
titanic.head()

'''
	survived	pclass	sex	age	sibsp	parch	fare	embarked	deck
0	0	3	male	22.0	1	0	7.2500	S	NaN
1	1	1	female	38.0	1	0	71.2833	C	C
2	1	3	female	26.0	0	0	7.9250	S	NaN
3	1	1	female	35.0	1	0	53.1000	S	C
4	0	3	male	35.0	0	0	8.0500	S	NaN
'''
# 생존율 구하기
titanic.groupby(['sex', 'pclass']).survived.transform('mean')

'''
0      0.135447
1      0.968085
2      0.500000
3      0.968085
4      0.135447
         ...   
886    0.157407
887    0.968085
888    0.500000
889    0.368852
890    0.135447
Name: survived, Length: 891, dtype: float64
'''
titanic.groupby(['sex', 'pclass']).survived.mean()

'''
sex     pclass
female  1         0.968085
        2         0.921053
        3         0.500000
male    1         0.368852
        2         0.157407
        3         0.135447
Name: survived, dtype: float64
'''
titanic['group_surv_rate'] = titanic.groupby(['sex', 'pclass']).survived.transform('mean')

'''
0      0.135447
1      0.968085
2      0.500000
3      0.968085
4      0.135447
         ...   
886    0.157407
887    0.968085
888    0.500000
889    0.368852
890    0.135447
Name: survived, Length: 891, dtype: float64
'''
titanic['outliers'] = abs(titanic.survived-titanic.group_surv_rate)

'''
0      0.135447
1      0.031915
2      0.500000
3      0.031915
4      0.135447
         ...   
886    0.157407
887    0.031915
888    0.500000
889    0.631148
890    0.135447
Length: 891, dtype: float64
'''
# 0.85보다 큰 값을 필터링해서 출력

titanic.loc[titanic.outliers > 0.85]

'''
	survived	pclass	sex	age	sibsp	parch	fare	embarked	deck	group_surv_rate	outliers
36	1	3	male	NaN	0	0	7.2292	C	NaN	0.135447	0.864553
41	0	2	female	27.00	1	0	21.0000	S	NaN	0.921053	0.921053
65	1	3	male	NaN	1	1	15.2458	C	NaN	0.135447	0.864553
74	1	3	male	32.00	0	0	56.4958	S	NaN	0.135447	0.864553
81	1	3	male	29.00	0	0	9.5000	S	NaN	0.135447	0.864553
107	1	3	male	NaN	0	0	7.7750	S	NaN	0.135447	0.864553
125	1	3	male	12.00	1	0	11.2417	C	NaN	0.135447	0.864553
127	1	3	male	24.00	0	0	7.1417	S	NaN	0.135447	0.864553
146	1	3	male	27.00	0	0	7.7958	S	NaN	0.135447	0.864553
165	1	3	male	9.00	0	2	20.5250	S	NaN	0.135447	0.864553
177	0	1	female	50.00	0	0	28.7125	C	C	0.968085	0.968085
199	0	2	female	24.00	0	0	13.0000	S	NaN	0.921053	0.921053
204	1	3	male	18.00	0	0	8.0500	S	NaN	0.135447	0.864553
207	1	3	male	26.00	0	0	18.7875	C	NaN	0.135447	0.864553
220	1	3	male	16.00	0	0	8.0500	S	NaN	0.135447	0.864553
261	1	3	male	3.00	4	2	31.3875	S	NaN	0.135447	0.864553
267	1	3	male	25.00	1	0	7.7750	S	NaN	0.135447	0.864553
271	1	3	male	25.00	0	0	0.0000	S	NaN	0.135447	0.864553
283	1	3	male	19.00	0	0	8.0500	S	NaN	0.135447	0.864553
286	1	3	male	30.00	0	0	9.5000	S	NaN	0.135447	0.864553
297	0	1	female	2.00	1	2	151.5500	S	C	0.968085	0.968085
301	1	3	male	NaN	2	0	23.2500	Q	NaN	0.135447	0.864553
312	0	2	female	26.00	1	1	26.0000	S	NaN	0.921053	0.921053
338	1	3	male	45.00	0	0	8.0500	S	NaN	0.135447	0.864553
348	1	3	male	3.00	1	1	15.9000	S	NaN	0.135447	0.864553
357	0	2	female	38.00	0	0	13.0000	S	NaN	0.921053	0.921053
391	1	3	male	21.00	0	0	7.7958	S	NaN	0.135447	0.864553
400	1	3	male	39.00	0	0	7.9250	S	NaN	0.135447	0.864553
414	1	3	male	44.00	0	0	7.9250	S	NaN	0.135447	0.864553
429	1	3	male	32.00	0	0	8.0500	S	E	0.135447	0.864553
444	1	3	male	NaN	0	0	8.1125	S	NaN	0.135447	0.864553
455	1	3	male	29.00	0	0	7.8958	C	NaN	0.135447	0.864553
489	1	3	male	9.00	1	1	15.9000	S	NaN	0.135447	0.864553
498	0	1	female	25.00	1	2	151.5500	S	C	0.968085	0.968085
509	1	3	male	26.00	0	0	56.4958	S	NaN	0.135447	0.864553
510	1	3	male	29.00	0	0	7.7500	Q	NaN	0.135447	0.864553
553	1	3	male	22.00	0	0	7.2250	C	NaN	0.135447	0.864553
569	1	3	male	32.00	0	0	7.8542	S	NaN	0.135447	0.864553
579	1	3	male	32.00	0	0	7.9250	S	NaN	0.135447	0.864553
622	1	3	male	20.00	1	1	15.7417	C	NaN	0.135447	0.864553
643	1	3	male	NaN	0	0	56.4958	S	NaN	0.135447	0.864553
664	1	3	male	20.00	1	0	7.9250	S	NaN	0.135447	0.864553
692	1	3	male	NaN	0	0	56.4958	S	NaN	0.135447	0.864553
709	1	3	male	NaN	1	1	15.2458	C	NaN	0.135447	0.864553
744	1	3	male	31.00	0	0	7.9250	S	NaN	0.135447	0.864553
751	1	3	male	6.00	0	1	12.4750	S	E	0.135447	0.864553
762	1	3	male	20.00	0	0	7.2292	C	NaN	0.135447	0.864553
772	0	2	female	57.00	0	0	10.5000	S	E	0.921053	0.921053
788	1	3	male	1.00	1	2	20.5750	S	NaN	0.135447	0.864553
803	1	3	male	0.42	0	1	8.5167	C	NaN	0.135447	0.864553
804	1	3	male	27.00	0	0	6.9750	S	NaN	0.135447	0.864553
821	1	3	male	27.00	0	0	8.6625	S	NaN	0.135447	0.864553
828	1	3	male	NaN	0	0	7.7500	Q	NaN	0.135447	0.864553
838	1	3	male	32.00	0	0	56.4958	S	NaN	0.135447	0.864553
854	0	2	female	44.00	1	0	26.0000	S	NaN	0.921053	0.921053
869	1	3	male	4.00	1	1	11.1333	S	NaN	0.135447	0.864553
'''


## Replacing NA Values by group-specific Values

import pandas as pd
titanic = pd.read_csv('titanic.csv')
titanic.head(2)

'''
    survived	pclass	sex	age	sibsp	parch	fare	embarked	deck
0	0	3	male	22.0	1	0	7.2500	S	NaN
1	1	1	female	38.0	1	0	71.2833	C	C
'''
titanic.info()

'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 9 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   survived  891 non-null    int64  
 1   pclass    891 non-null    int64  
 2   sex       891 non-null    object 
 3   age       714 non-null    float64
 4   sibsp     891 non-null    int64  
 5   parch     891 non-null    int64  
 6   fare      891 non-null    float64
 7   embarked  889 non-null    object 
 8   deck      203 non-null    object 
dtypes: float64(2), int64(4), object(3)
memory usage: 62.8+ KB
'''
titanic.age.mean()
# 29.69911764705882
# mean()은 다른것과 비교할만한 구성요소로써 작용할때는 구성비율
# 단일 컬럼으로써 작동할때는 '평균'의 의미가 있는 것 같다.
mean_age = titanic.age.mean()
# NaN 결측값을 age 평균으로 채워준다.
titanic.age.fillna(mean_age)

# 그렇지만 inplace = True를 해주어야 진정한 변경이 된다.
# 그 증거로
titanic.loc[titanic.age == 'NaN']
# 라고 aga의 값이 결측값인 행에 대해서 출력하라고 했을때 아무값도 안나온다.
titanic.groupby(['sex', 'pclass']).age.mean()
'''
sex     pclass
female  1         34.611765
        2         28.722973
        3         21.750000
male    1         41.281386
        2         30.740707
        3         26.507589
Name: age, dtype: float64
'''
titanic['group_mean_age'] = titanic.groupby(['sex', 'pclass']).age.transform('mean')

'''
0      26.507589
1      34.611765
2      21.750000
3      34.611765
4      26.507589
         ...    
886    30.740707
887    34.611765
888    21.750000
889    41.281386
890    26.507589
Name: age, Length: 891, dtype: float64
'''
titanic.head(2)

'''
    survived	pclass	sex	age	sibsp	parch	fare	embarked	deck	group_mean_age
0	0	3	male	22.0	1	0	7.2500	S	NaN	26.507589
1	1	1	female	38.0	1	0	71.2833	C	C	34.611765
'''
titanic.loc[titanic.age == 'NaN']
titanic.age.fillna(titanic.group_mean_age, inplace = True)


## Genenralizing split-apply-combine with apply()

import pandas as pd
titanic = pd.read_csv('titanic.csv', usecols = ['survived', 'pclass', 'sex', 'age', 'fare'])
titanic.head()

'''
    survived	pclass	sex	age	fare
0	0	3	male	22.0	7.2500
1	1	1	female	38.0	71.2833
2	1	3	female	26.0	7.9250
3	1	1	female	35.0	53.1000
4	0	3	male	35.0	8.0500
'''
titanic.groupby('sex').mean()

'''

    survived	pclass	age	fare
sex				
female	0.742038	2.159236	27.915709	44.479818
male	0.188908	2.389948	30.726645	25.523893
'''


## Generalizing split-apply-combine with apply()

import pandas as pd
titanic = pd.read_csv('titanic.csv', usecols = ['survived', 'pclass', 'sex', 'age', 'fare'])
titanic.head()

'''
    survived	pclass	sex	age	fare
0	0	3	male	22.0	7.2500
1	1	1	female	38.0	71.2833
2	1	3	female	26.0	7.9250
3	1	1	female	35.0	53.1000
4	0	3	male	35.0	8.0500
'''
titanic.groupby('sex').mean()

'''
    survived	pclass	age	fare
sex				
female	0.742038	2.159236	27.915709	44.479818
male	0.188908	2.389948	30.726645	25.523893
'''
female_group = list(titanic.groupby('sex'))[0][1]

'''
    survived	pclass	sex	age	fare
1	1	1	female	38.0	71.2833
2	1	3	female	26.0	7.9250
3	1	1	female	35.0	53.1000
8	1	3	female	27.0	11.1333
9	1	2	female	14.0	30.0708
...	...	...	...	...	...
880	1	2	female	25.0	26.0000
882	0	3	female	22.0	10.5167
885	0	3	female	39.0	29.1250
887	1	1	female	19.0	30.0000
888	0	3	female	NaN	23.4500
314 rows × 5 columns
'''
female_group.mean()

'''
survived     0.742038
pclass       2.159236
age         27.915709
fare        44.479818
dtype: float64
'''
def group_mean(group):
    return group.mean()
group_mean(female_group)

'''
survived     0.742038
pclass       2.159236
age         27.915709
fare        44.479818
dtype: float64
'''
# groupby 객체에 적용하고자 하는 사용자 정의 함수 사용 방법
titanic.groupby('sex').apply(group_mean)

'''
	survived	pclass	age	fare
sex				
female	0.742038	2.159236	27.915709	44.479818
male	0.188908	2.389948	30.726645	25.523893
'''

# mean은 근본적으로 평균 인것같다.
# 그래서 그 평균들이 뭉쳐있는 곳 사이에서 보이기를 비율처럼 보였을 수도 있고, 아닐수도있고
# 하지만 근본적으로 평균이다.
titanic.nlargest(5, 'age')

'''
	survived	pclass	sex	age	fare
630	1	1	male	80.0	30.0000
851	0	3	male	74.0	7.7750
96	0	1	male	71.0	34.6542
493	0	1	male	71.0	49.5042
116	0	3	male	70.5	7.7500
'''
# 생존자 중에서 나이가 많은 순서대로 5명을 구하는 것
def five_oldest_surv(group):
    return group[group.survived == 1].nlargest(5, 'age')
titanic.groupby('sex').apply(five_oldest_surv)

'''

    survived	pclass	sex	age	fare
sex						
female	275	1	1	female	63.0	77.9583
483	1	3	female	63.0	9.5875
829	1	1	female	62.0	80.0000
366	1	1	female	60.0	75.2500
11	1	1	female	58.0	26.5500
male	630	1	1	male	80.0	30.0000
570	1	2	male	62.0	10.5000
587	1	1	male	60.0	79.2000
647	1	1	male	56.0	35.5000
449	1	1	male	52.0	30.5000
'''


## Hierarchical Indexing (MultiIndex) with Groupby
- Hierarchical는 '계급에 따른' 이라는 뜻이다.

import pandas as pd
titanic = pd.read_csv('titanic.csv', usecols = ['survived', 'pclass', 'sex', 'age', 'fare'])
titanic

'''

    survived	pclass	sex	age	fare
0	0	3	male	22.0	7.2500
1	1	1	female	38.0	71.2833
2	1	3	female	26.0	7.9250
3	1	1	female	35.0	53.1000
4	0	3	male	35.0	8.0500
...	...	...	...	...	...
886	0	2	male	27.0	13.0000
887	1	1	female	19.0	30.0000
888	0	3	female	NaN	23.4500
889	1	1	male	26.0	30.0000
890	0	3	male	32.0	7.7500
891 rows × 5 columns
'''
summary = titanic.groupby(['sex', 'pclass']).mean()

'''

        survived	age	fare
sex	pclass			
female	1	0.968085	34.611765	106.125798
2	0.921053	28.722973	21.970121
3	0.500000	21.750000	16.118810
male	1	0.368852	41.281386	67.226127
2	0.157407	30.740707	19.741782
3	0.135447	26.507589	12.661633
'''
summary.index

'''
MultiIndex([('female', 1),
            ('female', 2),
            ('female', 3),
            (  'male', 1),
            (  'male', 2),
            (  'male', 3)],
           names=['sex', 'pclass'])
'''
summary.loc[('female', 2),:]

'''
survived     0.921053
age         28.722973
fare        21.970121
Name: (female, 2), dtype: float64
'''

# summary.loc[['female', 2],:]
# 라고 하니깐, 2는 인덱스에 안들어있다면서 keyerror가 떴다.
summary.loc[('female', 2), 'age']
# 28.722972972972972
summary.swaplevel().sort_index()

'''

        survived	age	fare
pclass	sex			
1	female	0.968085	34.611765	106.125798
male	0.368852	41.281386	67.226127
2	female	0.921053	28.722973	21.970121
male	0.157407	30.740707	19.741782
3	female	0.500000	21.750000	16.118810
male	0.135447	26.507589	12.661633
'''
summary.reset_index()

'''
    sex	pclass	survived	age	fare
0	female	1	0.968085	34.611765	106.125798
1	female	2	0.921053	28.722973	21.970121
2	female	3	0.500000	21.750000	16.118810
3	male	1	0.368852	41.281386	67.226127
4	male	2	0.157407	30.740707	19.741782
5	male	3	0.135447	26.507589	12.661633
'''


## stack() and unstack()

import pandas as pd
summer = pd.read_csv('summer.csv')
summer.head()

'''
    Year	City	Sport	Discipline	Athlete	Country	Gender	Event	Medal
0	1896	Athens	Aquatics	Swimming	HAJOS, Alfred	HUN	Men	100M Freestyle	Gold
1	1896	Athens	Aquatics	Swimming	HERSCHMANN, Otto	AUT	Men	100M Freestyle	Silver
2	1896	Athens	Aquatics	Swimming	DRIVAS, Dimitrios	GRE	Men	100M Freestyle For Sailors	Bronze
3	1896	Athens	Aquatics	Swimming	MALOKINIS, Ioannis	GRE	Men	100M Freestyle For Sailors	Gold
4	1896	Athens	Aquatics	Swimming	CHASAPIS, Spiridon	GRE	Men	100M Freestyle For Sailors	Silver
'''
medals_by_country = summer.groupby(['Country', 'Medal']).Medal.count()

'''
Country  Medal 
AFG      Bronze     2
AHO      Silver     1
ALG      Bronze     8
         Gold       5
         Silver     2
                   ..
ZIM      Gold      18
         Silver     4
ZZX      Bronze    10
         Gold      23
         Silver    15
Name: Medal, Length: 347, dtype: int64
'''
medals_by_country.shape
# (347,)
medals_by_country.unstack()

'''

Medal	Bronze	Gold	Silver
Country			
AFG	2.0	NaN	NaN
AHO	NaN	NaN	1.0
ALG	8.0	5.0	2.0
ANZ	5.0	20.0	4.0
ARG	91.0	69.0	99.0
...	...	...	...
VIE	NaN	NaN	2.0
YUG	118.0	143.0	174.0
ZAM	1.0	NaN	1.0
ZIM	1.0	18.0	4.0
ZZX	10.0	23.0	15.0
147 rows × 3 columns
'''
# level = -1이라는 의미는 -1까지 수준의 값을
# 가장 오른쪽에 있는 값을
# fill_value로써 채워준다는 뜻이다.

medals_by_country = medals_by_country.unstack(level = -1, fill_value = 0)

'''

Medal	Bronze	Gold	Silver
Country			
AFG	2	0	0
AHO	0	0	1
ALG	8	5	2
ANZ	5	20	4
ARG	91	69	99
...	...	...	...
VIE	0	0	2
YUG	118	143	174
ZAM	1	0	1
ZIM	1	18	4
ZZX	10	23	15
147 rows × 3 columns
'''
medals_by_country.shape
# (147, 3)
medals_by_country = medals_by_country[['Gold', 'Silver', 'Bronze']]

'''

Medal	Gold	Silver	Bronze
Country			
AFG	0	0	2
AHO	0	1	0
ALG	5	2	8
ANZ	20	4	5
ARG	69	99	91
...	...	...	...
VIE	0	2	0
YUG	143	174	118
ZAM	0	1	1
ZIM	18	4	1
ZZX	23	15	10
147 rows × 3 columns
'''
medals_by_country.sort_values(by = ['Gold', 'Silver', 'Bronze'], ascending= [False, False, False], inplace = True)
# ascending 에 False를 하나만 넣어주었더니
# ValueError: Length of ascending (1) != length of by (3)
# 에러가 나왔다.
medals_by_country.head()

'''
Medal	Gold	Silver	Bronze
Country			
USA	2235	1252	1098
URS	838	627	584
GBR	546	621	553
ITA	476	416	404
GER	452	378	475
'''
medals_by_country.loc[('USA', 'Gold')]
# 2235
import matplotlib.pyplot as plt
plt.style.use('seaborn')
medals_by_country.head(10).plot(kind = 'bar', figsize = (14,10), fontsize = 14)
plt.xlabel('Country', fontsize = 14)
plt.ylabel('Medals', fontsize = 14)
plt.title('Medals per Country', fontsize = 15)
plt.legend(fontsize = 15)
plt.show()



# 이번챕터를 통해서 이해가 안된 것

 

apply

 

- 함수적용의 연결고리 같은건데 형식은

df.apply(선언함수)

이렇게 사용된다.

 

그러면 선언함수에 df의 각각의 행에 해당하는 값이 들어가서

나오게 된다.



transform

agg

이 두개는 같이 가는 게 좋다.

 

agg는 nunique의 개념과도 흡사하다.
# 한 개념당의 점수만 보여준다.

# 하지만 transform은 각 개념에 대해서 값을 구해주고, 나열된 행만큼 출력이 된다.

 

df1

'''
    이름	과목	점수	과목평균
0	송중기	국어	89	86.00
1	김나현	영어	42	47.00
2	박효신	국어	78	86.00
3	김범수	영어	61	47.00
4	권보아	국어	91	86.00
5	한동근	영어	38	47.00
'''
## agg

df1.groupby('과목')['점수'].agg('mean')

'''
과목
국어   86.00
영어   47.00
Name: 점수, dtype: float64
'''
# transform

df1['과목평균'] = df1.groupby('과목')['점수'].transform('mean')

'''
0   86.00
1   47.00
2   86.00
3   47.00
4   86.00
5   47.00
Name: 점수, dtype: float64
'''
# agg merge 버전 merge

df4= df2.groupby('과목')['점수'].agg('mean').reset_index()
df2.merge(df4, on = '과목', how = 'left')

'''
    이름	과목	점수_x	점수_y
0	송중기	국어	89	86.00
1	김나현	영어	42	47.00
2	박효신	국어	78	86.00
3	김범수	영어	61	47.00
4	권보아	국어	91	86.00
5	한동근	영어	38	47.00
'''

# agg는 입력 후 출력하기는 쉽지만
# 뒷처리가 필요하다. 이를테면 컬럼명이 점수_x, 점수_y로 자동으로 반영되 나오기 때문이다.
# transform 버전 merge

# 반면, transform 은 컬럼명 지정이 가능하다.
# 익숙해지기가 약간 번거로울 뿐이지, 익숙해지고 나면 오히려 편하다.

# 평균과 표준편차를 더한 값을 리턴해줘라.

df3['m+s'] = df3.groupby('과목')['점수'].transform(lambda x:x.mean() + x.std())

'''
0   93.00
1   59.29
2   93.00
3   59.29
4   93.00
5   59.29
Name: 점수, dtype: float64
'''
# transform 필터

df3.loc[df3.groupby('과목')['점수'].transform(lambda x:x > x.mean())]

'''
    이름	과목	점수	m+s
0	송중기	국어	89	93.00
3	김범수	영어	61	59.29
4	권보아	국어	91	93.00
'''


swaplevel

 

# 정보가 많이 없다. 패스하자.



unstack

stack

 

# 흠.. 시간을 두고 더 공부해보자. 이것도 난해하다.

300x250