(8주차) 11월1일
tidydata, barplot, 해들리위컴의 그래프레이어, 심슨의역설
-
(1/8) tidydata (1)
-
(2/8) tidydata (2)
-
(3/8) tidydata (3)
-
(4/8) Barplot + 해들리위컴 그래프레이어 (1)
-
(5/8) Barplot + 해들리위컴 그래프레이어 (2)
-
(6/8) Barplot + 해들리위컴 그래프레이어 (2)
-
(7/8) 심슨의 역설 (1)
-
(8/8) 심슨의 역설 (2)
import pandas as pd
import numpy as np
from plotnine import *
import matplotlib.pyplot as plt
-
느낌: ggplot으로 그림 그리기 좋은 데이터 + pandas로 query, group by 등을 쓰기 좋은 자료
-
정의: https://r4ds.had.co.nz/tidy-data.html
- Each variable must have its own column.
- Each observation must have its own row.
- Each value must have its own cell.
예시1
obs | x | y | shape | color |
---|---|---|---|---|
0 | 0 | 0 | 'star' | 'F' |
1 | 0 | 1 | 'circ' | 'F' |
2 | 1 | 0 | 'star' | 'M' |
3 | 1 | 1 | 'circ' | 'M' |
예시2
shape=star | shape=circ | |
---|---|---|
color=F | (0,0) | (0,1) |
color=M | (1,0) | (1,1) |
-
문제의 깃헙주소로 들어가서 데이터를 관찰 $\to$ 좌측상단이 비워져있음 $\to$ index_col=0
옵션을 사용
url = 'https://raw.githubusercontent.com/PacktPublishing/Pandas-Cookbook/master/data/state_fruit.csv'
df=pd.read_csv(url,index_col=0)
df
-
데이터변형
df.stack()
df.stack().reset_index()
df.stack().reset_index().rename(columns={'level_0':'group1','level_1':'group2',0:'X'})
-
index_col=0
옵션을 사용하지않음
url = 'https://raw.githubusercontent.com/PacktPublishing/Pandas-Cookbook/master/data/state_fruit.csv'
df2=pd.read_csv(url)
df2
df2.rename(columns={'Unnamed: 0':'group1'})
df2.rename(columns={'Unnamed: 0':'group1'}).melt(id_vars='group1')
df2.rename(columns={'Unnamed: 0':'group1'}).melt(id_vars='group1')\
.rename(columns={'variable':'group2','value':'X'})
df
df.melt()
df2
df2.stack()
df
df.reset_index()
df.reset_index().melt(id_vars='index')
df.reset_index().melt(id_vars='index')\
.rename(columns={'index':'group1','variable':'group2','value':'X'})
df2.set_index('Unnamed: 0')
df2.set_index('Unnamed: 0').stack()
df2.set_index('Unnamed: 0').stack().reset_index()
df2.set_index('Unnamed: 0').stack().reset_index()\
.rename(columns={'Unnamed: 0':'group1','level_1':'group2',0:'X'})
g=['A']*100+['B']*200
y=list(np.random.randn(100)*2+2)+list(np.random.randn(200)+3)
df=pd.DataFrame({'g':g,'y':y})
df
ggplot(df)+geom_bar(aes(x='g',fill='g')) ## 디폴트로 카운트를 수행해줌
-
이것은 아래의 코드와 같다.
df.groupby(by='g').count()
fig=ggplot(df.groupby(by='g').count().reset_index())
fig+geom_bar(aes(x='g',y='y',fill='g'),stat='identity')
-
barplot은 기본적으로 groupby+count()가 내장되어 있다. 따라서 아래의 코드
ggplot(df)+geom_bar(aes(x='g',fill='g')) ## 디폴트로 카운트를 수행해줌
를 좀더 엄밀하게 쓰면
ggplot(df)+geom_bar(aes(x='g',fill='g'),stat='count')
-
이것은 때때로 불편하다. 왜냐하면 데이터프레임을 변환하는 것은 판다스를 이용하는게 더 쉽고 자유로움
td=df.groupby(by='g').count().reset_index()
td
-
그냥 'x=g, y=y'를 맵핑하여 그리면 안되나?
plt.bar(td.g,td.y)
td.plot(kind='bar',x='g',y='y')
-
그런데 ggplot을 쓰려고 하면?
ggplot(td)+geom_bar(aes(x='g',y='y',fill='g'))
- 너무 불편해요.. stat='identity' 를 항상 써야하는것이!
-
groupby 를 자동으로 해주므로 익숙해지면 ggplot2 방식이 더 편하지 않을까? $\to$ groupby 하는게 더 편해요..
df.groupby('g').agg({'y':[np.mean,np.median,np.std,lambda x: np.max(x)-np.min(x)]})
df.groupby('g')\
.agg({'y':[np.mean,np.median,np.std,lambda x: np.max(x)-np.min(x)]})\
.rename(columns={'<lambda_0>':'range'}).stack().reset_index()
td=df.groupby('g')\
.agg({'y':[np.mean,np.median,np.std,lambda x: np.max(x)-np.min(x)]})\
.rename(columns={'<lambda_0>':'range'}).stack().reset_index()
ggplot(td)+geom_bar(aes(x='level_1',y='y',fill='g'),stat='identity')
- 쌓인상태로 보이는것이 불편함. $\to$ position='dodge' 로!
ggplot(td)+geom_bar(aes(x='level_1',y='y',fill='g'),stat='identity',position='dodge')
-
때때로 아래와 같이 보는 것이 더 좋은 경우도 있음
ggplot(td)\
+geom_bar(aes(x='level_1',y='y',fill='g'),stat='identity',position='dodge')\
+coord_flip()
ggplot(td)\
+geom_bar(aes(x='level_1',y='y',fill='g'),stat='identity',position='dodge')\
+coord_flip()+facet_wrap('level_1')
ggplot(td)\
+geom_bar(aes(x='g',y='y',fill='g'),stat='identity',position='dodge')\
+coord_flip()+facet_wrap('level_1')
ggplot(td)+facet_grid('level_1~g')\
+geom_bar(aes(x='g',y='y',fill='g'),stat='identity',position='dodge')+coord_flip()
-
데이터셋 + 맵핑 + 지옴 + 포지션 + 스탯 + 축 + 면분할
- 데이터셋: 판다스
- 맵핑: x축, y축, 색깔, 크기, 투명도
- 지옴: 포인트지옴, 바지옴, 라인지옴, 스무스지옴
- 포지션: jitter, dodge, intentity
- 스탯: identity, count
- 축: coord_flip()
- 면분할: facet_wrap(), facet_grid()
DEP=(['A1']*2+['A2']*2+['B1']*2+['B2']*2)*2
GEN=['M']*8+['F']*8
STATE=['PASS','FAIL']*8
COUNT=[1,9,2,8,80,20,85,15,5,5,5,5,9,1,9,1]
df=pd.DataFrame({'DEP':DEP,'STATE':STATE,'GEN':GEN,'COUNT':COUNT})
df
df.groupby(['GEN','STATE']).agg({'COUNT':np.sum})
df.groupby(['GEN','STATE']).agg({'COUNT':np.sum})
df.groupby(['GEN','STATE']).agg({'COUNT':np.sum}).reset_index()
df.groupby(['GEN']).agg({'COUNT':np.sum}).reset_index()
-
두개의 데이터프레임을 합쳐야 한다.
_df1=df.groupby(['GEN','STATE']).agg({'COUNT':np.sum}).reset_index()
_df2=df.groupby(['GEN']).agg({'COUNT':np.sum}).reset_index().rename(columns={'COUNT':'SUM'})
display(_df1)
display(_df2)
-
단순한 방법
def f(x):
if x=='F':
return 40
if x=='M':
return 220
_df1['SUM']=list(map(f,_df1.GEN))
_df1
-
좀 더 좋은 방법
_df1=df.groupby(['GEN','STATE']).agg({'COUNT':np.sum}).reset_index()
- _df1를 다시 롤백
def f(_df2):
return lambda x: _df2.query('GEN == @x').SUM.item()
_df1.GEN
_df1['SUM']=list(map(f(_df2),_df1.GEN))
_df1
-
더 좋은 방법
_df1=df.groupby(['GEN','STATE']).agg({'COUNT':np.sum}).reset_index()
- _df1을 다시 롤백
_df1
_df2
pd.merge(_df1,_df2)
_df1.merge(_df2)
_df2.merge(_df1)
td=_df2.merge(_df1)
td
td['PROP']=td.COUNT/td.SUM
td
ggplot(td.query('STATE=="PASS"'))+geom_bar(aes(x='GEN',y='PROP',fill='GEN'),stat='identity')
-
남자의 합격률이 더 높다. $\to$ 성차별이 있어보인다(?)
-
학과별 합격률
df
td=df.groupby(['DEP','GEN']).agg({'COUNT':sum}).reset_index()\
.rename(columns={'COUNT':'SUM'}).merge(df)
td['PROP']=td.COUNT/td.SUM
td
td.query('STATE=="PASS"')
ggplot(td.query('STATE=="PASS"'))\
+geom_bar(aes(x='GEN',y='PROP',fill='GEN'),stat='identity')\
+facet_wrap('DEP')