캐글 대회에 참가하면서 파이썬 문법이나 plotly의 특성에 대해 구글링하는 시간이 훨씬 늘었다. 앞으로도 대회에 참가하거나 시각화를 할 때에 이렇게나 많은 특성을 모두 외울수는 없으니 구글링을 하게 될 텐데, 자주쓰는 속성이나 기본틀에 대해서는 포스팅을 해두고 바로바로 찾아보는것이 좋겠다는 생각이 들었다.
그래서 이번 포스팅에서는 앞으로 자주 사용하게 될 차트 중 하나인 막대그래프의 기본 틀과 자주 사용되는 속성에 대해 포스팅 하려 한다.
필요한 라이브러리를 import 해주고 데이터를 불러와 저장해준다. 필자는 캐글노트북에서 작성하여 data add 기능으로 kaggle에 있는 데이터를 바로 불러왔지만 로컬이나 colab, jupyter등의 다른 노트북을 사용중이라면 데이터를 다운받은후 괄호안의 경로를 재설정해주어야 한다.
출력할 데이터 확인
1
df21[0:5][Q1,Q14] # Q1,Q14 항목의 값 0부터 4까지 출력
ㅤ
Q1
Q3
0
What is your age (# years)?
In which country do you currently reside?
1
50-54
India
2
50-54
Indonesia
3
22-24
Pakistan
4
45-49
Mexico
이렇게 출력하거나
1
df21[df21['Q3'] == "South Korea"] # Q3 항목의 값이 "South Korea"인 행의 데이터프레임 출력
fig.update_layout(barmode='group', title_text='2021, Korea and Japan age distribution', showlegend=True)
fig.update_xaxes(title_text='Korea Age distribution', row=1, col=1) fig.update_yaxes(title_text='Counts', row=1, col=1) fig.update_xaxes(title_text='Japan Age distribution', row=1, col=2) fig.update_yaxes(title_text='Counts', row=1, col=2)
fig.show()
add_trace, update_layout 등의 파라미터(속성)값으로 그래프를 꾸미는 코드이다.
fig.add_trace(go.Bar(name='Korea', x=KR_Age.index, y=KR_Age.values, marker_color='red'),1,1)부터 살펴보면 go.Bar는 막대그래프를 뜻한다, 각 파라미터를 살펴보면 name는 그래프에 표현되는 항목의 이름, x는 그래프의 x축이 나타낼 항목, y는 그래프의 y축이 나타낼 값, marker_color은 그래프의 색을 표현하며 가장 끝의1,1는 위 항목이 표시될 그래프의 행,열을 뜻한다.(그러니 1번째 행 1번째 열의 그래프에 Korea 항목이 들어간다.)
fig.update_layout(barmode='group', title_text='2021, Korea and Japan age distribution', showlegend=True)도 뜯어보겠다. barmode는 한 그래프에 여러 항목이 들어있을 경우 어떻게 나타내는지에 대한 속성인데 대표적으로 group과stack이 있다. title_text는 제목, showlegend는 범례 표기 여부를 가리킨다.
마지막의 fig.show()는 fig객체를 출력하는 코드이다.
아래 References에 막대그래프의 파라미터가 정리되있는 링크를 걸어놓을테니 활용하여 다양하게 그래프를 만들어보자.
그림처럼 마커의 모양을 변경 할 수 있다. color옵션은 헥스코드와 rgb값 모두 가능하며, 모양의 경우 매우 다양하므로 포스팅 최하단에 링크를 첨부한다.
박스플롯 (box plot)
박스플롯이란??
‘박스플롯’box plot,또는 ‘상자 수염 그림’은 수치적 자료를 나타내는 그래프이다. 이 그래프는 가공하지 않은 자료 그대로를 이용하여 그린 것이 아니라, 자료로부터 얻어낸 통계량인 ‘5가지 요약 수치’five-number summary 를 가지고 그린다. 이 때 5갸지 요약 수치란 최솟값, 제 1사분위(Q1),제 2사분위(Q2), 제 3사분위(Q3), 최댓값을 일컫는 말이다. 히스토그램과는 다르게 집단이 여러개인 경우에도 한 공간에 수월하게 나타낼수 있다.
언제 사용되는가??
박스 플롯을 사용하는 이유는 데이터가 매우 많을때 모든 데이터를 한눈에 확인하기 어려우니 그림을 이용해 데이터 집합의 범위와 중앙값을 빠르게 확인 할 수 있는 목적으로 사용한다. 또한, 통계적으로 이상치outlier가 있는지도 확인이 가능하다.
박스플롯의 구성
박스플롯을 처음 접한다면 박스플롯을 어떻게 해석해야 하는지 난해할수 있다. 다음 그림에서 박스플롯의 각 구성이 무엇을 의미하는지 간단하게 알아보자.
각 요소들을 설명하면 다음과 같다.
요소
설명
이상치(outlier)
최소값보다 작은데이터 또는 최대값보다 큰 데이터가 이상치에 해당한다
최대값(upper whisker)
‘중앙값 + 1.5 × IQR’보다 작은 데이터 중 가장 큰 값
최소값(lower whisker)
‘중앙값 - 1.5 × IQR’보다 큰 데이터 중 가장 작은 값
IQR(Inter Quartile Range)
제3사분위수 - 제1사분위수 실수 값 분포에서 1사분위수(Q1)와 3사분위수(Q3)를 뜻하고 이 3사분위수와 1사분위수의 차이(Q3 - Q1)를 IQR라고 한다.
중앙값
박스내부의 가로선, 용어 그대로 중앙값이다
whisker
상자의 상하로 뻗어있는 선
Plotly로 박스플롯 작성하기
1 2 3 4 5 6 7 8 9 10 11
import numpy as np import pandas as pd import matplotlib.pyplot as plt
df3 = pd.DataFrame(np.random.randint(0, 100, (100, 2)), columns=['A', 'B']) #임의로 수 생성
plt.figure(figsize=(8,7)) # 크기 지정 boxplot = df3.boxplot(column=['B']) # df3의 'B'컬럼을 박스플롯으로 생성 plt.yticks(np.arange(0,101,step=5)) # 박스플롯이 그려질 범위 지정 plt.show()
# This Python 3 environment comes with many helpful analytics libraries installed # It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python # For example, here's several helpful packages to load
import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
# Input data files are available in the read-only "../input/" directory # For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory
import os for dirname, _, filenames in os.walk('/kaggle/input'): for filename in filenames: print(os.path.join(dirname, filename))
# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" # You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
1 2
from plotly.offline import plot, iplot, init_notebook_mode init_notebook_mode(connected=True)
1 2 3 4 5 6 7 8 9 10 11 12
import numpy as np import pandas as pd import matplotlib.pyplot as plt import plotly.express as px import plotly.graph_objects as go from warnings import filterwarnings filterwarnings('ignore')
fig = go.Figure(data=[ -> 그래프의 기본적인 틀을 설정하는 함수이고, fig에 데이터를 부여해준다.
go.Pie( -> 그래프의 형태가 파이모양(원그래프)임을 의미한다.
labels = df['Q2'][1:].value_counts().index -> 그래프로 표현될 값에 붙일 이름이다. 이 코드에서는 ‘df’의 ‘Q2’열에 있는 데이터의 ‘1번인덱스(질문열을 제외하기 위함) 행부터 마지막까지’ 카운트하고 값에따라 분류했을 때의 인덱스열 인 것이다.
values = df['Q2'][1:].value_counts().values -> 그래프로 표현될 값이다. 이 코드에서는 ‘df’의 ‘Q2’열에 있는 데이터의 ‘1번인덱스 행부터 마지막까지’ 카운트하고 값에따라 분류했을 때의 값(각 값을 카운트한 값) 이다.
textinfo = 'label+percent' -> 그래프에 표시된 항목에 나타낼 텍스트를 설정한다. 이 코드에서는 항목명(label)과 비율(percent)을 나타낸다.
1 2 3 4 5 6
fig.update_traces( marker=dict(colors=colors[2:])) # 그래프를 어떤 색으로 표현할 것인가를 설정 fig.update_layout( # 그래프의 부가정보 설정 title_text='Gender Distribution', # 그래프 제목 showlegend=False) # 범례표시여부 fig.show()
fig.update_traces( -> 추가바람
marker=dict(colors=colors[2:])) -> 그래프의 색상을 설정한다. 이 코드에서는 위에서 설정된 colors 리스트에서 2번 인덱스부터 순서대로 사용한다.
fig.update_layout( -> 그래프의 부가정보를 설정한다.
title_text='Gender Distribution' -> 그래프의 제목을 설정한다.
showlegend=False) -> 범례의 표기여부를 결정한다. 이 코드에서는 False이므로 범례가 표기되지 않는다.
fig.show() -> 화면에 그래프를 표시하는 기능을한다. 몇몇에디터에서는 자동으로 표시되기때문에 호출할 필요가 없는 경우가 있다.
man = df[df['Q2'] == 'Man']['Q1'].value_counts() # 성별이 남성[df['Q2'] == 'Man']인 행에서 나이['Q1']의 값을 카운트하여 시리즈로 만듦. woman = df[df['Q2'] == 'Woman']['Q1'].value_counts() # 성별이 여성[df['Q2'] == 'Woman']인 행에서 나이['Q1']의 값을 카운트하여 시리즈로 만듦. textonbar_man = [ # list comprehension = [(변수를 활용한 값) for (사용할 변수 이름) in (순회 할 수 있는 값)] round((m/(m+w))*100, 1) for m, w inzip(man.values, woman.values)] # for문을 사용하여 round함수의 계산을 하고 textonbar_man에 저장 textonbar_woman = [ # list comprehension round((w/(m+w))*100, 1) for m, w inzip(man.values, woman.values)]
# go = graph_objects fig = go.Figure(data=[ go.Bar( # 막대그래프 name='Man', # 그래프로 나타낼 항목 x=man.index, # x축에 man의 인덱스 y=man.values, # y축에 man의 값 text=textonbar_man, # 막대의 값을 작성해줄 텍스트 marker_color=colors[2]), #막대 색 go.Bar( name='Woman', x=woman.index, y=woman.values, text=textonbar_woman, marker_color=colors[3]) ]) fig.update_traces( texttemplate='%{text:.3s}%', # fig(print(fig)로 출력가능)내부의 text 인자를 차례대로 출력 (그래프의 위의 텍스트를 표현) textposition='inside') # 그래프상에서 값의 위치 fig.update_layout( barmode='stack', # 막대의 형태 title_text='Age distribution by gender', # 그래프 제목 xaxis_title='Age', # x축 제목 yaxis_title='Counts') # y축 제목 fig.show()
그래프
코드 해석
1 2 3 4
man = df[df['Q2'] == 'Man']['Q1'].value_counts() woman = df[df['Q2'] == 'Woman']['Q1'].value_counts() textonbar_man = [ round((m/(m+w))*100, 1) for m, w inzip(man.values, woman.values)] textonbar_woman = [ round((w/(m+w))*100, 1) for m, w inzip(man.values, woman.values)]
man = df[df['Q2'] == 'Man']['Q1'].value_counts() -> 성별이 남성[df['Q2'] == 'Man]인 행에서 나이['Q1']의 값을 카운트하여 시리즈를 생성한다.
woman = df[df['Q2'] == 'Woman']['Q1'].value_counts() -> 성별이 여성[df['Q2'] == 'Woman']인 행에서 나이['Q1']의 값을 카운트하여 시리즈를 생성한다.
textonbar_man = [round((m/(m+w))*100, 1) for m, w in zip(man.values, woman.values)] -> for문을 사용하여 round함수를 계산 하고 textonbar_man에 저장한다.
textonbar_woman = [round((uw/(m+w))*100, 1) for m, w in zip(man.values, woman.values)] -> for문을 사용하여 round함수를 계산 하고 textonbar_woman에 저장한다.
data에 담겨있는 애플의 주가정보 중 ‘Open’에 해당하는 전일 종가를 가장 앞쪽(.head())부터 출력한 것이다. 애플주식이 이렇게 쌌었나 검색해보니 이게 맞다….
방법 1. Pyplot API
1 2 3 4 5 6 7 8 9 10 11 12 13
# import fix_yahoo_finance as yf import yfinance as yf import matplotlib.pyplot as plt
data = yf.download('AAPL', '2019-11-01', '2021-11-01') ts = data['Open'] plt.figure(figsize=(10,6)) plt.plot(ts) plt.legend(labels=['Price'], loc='best') plt.title('Stock Market fluctuation of AAPL') plt.xlabel('Date') plt.ylabel('Stock Market Open Price') plt.show()
Output
[*********************100%***********************] 1 of 1 completed
이처럼 결과가 출력되지만 이 문법은 시각화를 처음배우는 초심자에게는 적합하지 않다고 한다. 후술할 문법과 위 문법 모두 출력은 되나 이 문법은 객체지향이 아니기도 하고 상대적으로 복잡하기때문에 초심자의 경우에 헷갈릴수 있어 사용하지 않는다. 구글링 했을때 객체.이 아닌 plt.으로 시작하는 애들이 있다면 그 코드는 스킵하는게 좋다.
방법 2. 객체지향 API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas from matplotlib.figure import Figure import matplotlib.pyplot as plt
fig = Figure()
import numpy as np np.random.seed(6) x = np.random.randn(20000)
.xticks()는 x축의 눈금을 나타내는 메소드인데 기본적으로는 list자료형 한개을 사용한다. 하지만 메소드에 인자가 ‘list’ 두 개로 받아졌을 경우, 첫번째 list는 x축 눈금의 갯수가 된다. 두번째 list는 x축 눈금의 이름이 된다. 이 코드에서는 rotation 옵션도 들어가 있는데 이것은 그냥 이름을 몇도정도 기울일지 나타낸다.
plot = ax.bar()는 그래프를 막대로 만든다. 첫번째 리스트 인자의 수 만큼 막대가 생성되고, 두번째 리스트 인자의 값 만큼 막대가 길어진다. 이렇다보니 첫번째 리스트와 두번째 리스트의 인자의 수가 일치해야 에러가 나지 않는다.
for문 내부의 ax.text()는 Seaborn-막대그래프-표현할 값이 한 개인 막대 그래프 챕터에 서술했으니 참고하길 바란다.
산점도 그래프
두개의 연속형 변수 (키, 몸무게 등)
상관관계 != 인과관계
나타내는 값이 한가지인 산점도 그래프
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import matplotlib.pyplot as plt import seaborn as sns
# 내장 데이터 tips = sns.load_dataset("tips") x = tips['total_bill'] y = tips['tip']
fig, ax = plt.subplots(figsize=(10, 6)) ax.scatter(x, y) # 각각의 값을 선으로 표현해주는 scatter() ax.set_xlabel('Total Bill') ax.set_ylabel('Tip') ax.set_title('Tip ~ Total Bill')
fig, ax = plt.subplots(figsize=(10, 6)) for label, data in tips.groupby('sex'): ax.scatter(data['total_bill'], data['tip'], label=label, color=data['sex_color'], alpha=0.5) ax.set_xlabel('Total Bill') ax.set_ylabel('Tip') ax.set_title('Tip ~ Total Bill by Gender')
ax.legend() fig.show()
Output
히스토그램
수치형 변수 1개
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import matplotlib.pyplot as plt import numpy as np import seaborn as sns
# 내장 데이터 titanic = sns.load_dataset('titanic') age = titanic['age']
nbins = 21 fig, ax = plt.subplots(figsize=(10, 6)) ax.hist(age, bins = nbins) # 여기서 bins = nbins는 히스토그램을 더 세밀하게 나누어 준다. ax.set_xlabel("Age") ax.set_ylabel("Frequency") ax.set_title("Distribution of Aae in Titanic") ax.axvline(x = age.mean(), linewidth = 2, color = 'r') fig.show()
Output
코드
설명
.hist()
데이터를 히스토그램으로 표현해주는 메소드
.axvline()
데이터의 평균을 선으로 나타내주는 메소드
박스플롯
x축 변수: 범주형 변수, 그룹과 관련있는 변수, 문자열
y축 변수: 수치형 변수
1 2 3 4 5 6 7 8 9 10 11 12 13
import matplotlib.pyplot as plt import seaborn as sns
iris = sns.load_dataset('iris')
data = [iris[iris['species']=="setosa"]['petal_width'], iris[iris['species']=="versicolor"]['petal_width'], iris[iris['species']=="virginica"]['petal_width']]
Fri 19
Thur 62
Sun 76
Sat 87
Name: day, dtype: int64
표현할 값이 한 개인 막대 그래프
1 2 3 4 5 6 7
# 기본주석 생략 ax = sns.countplot(x = "day", data = tips, order = tips['day'].value_counts().index) # x축을 'day'로 지정, data는 'tips'로 채워넣음, 'day'의 값이 높은 순서대로 막대그래프 정렬 for p in ax.patches: # ax.patches = p height = p.get_height() # 아래행을 실행하기위해 막대그래프의 높이 가져옴 ax.text(p.get_x() + p.get_width()/2., height+3, height, ha = 'center', size=9) # 막대그래프 위 수치 작성 ax.set_ylim(-5, 100) # y축 최소, 최대범위 plt.show()
Output
나중에 다시 본다면 조금 설명이 필요할 것 같다. 특히 ax.text행의 인자가 조금 많은데 설명이 필요한 듯하다. 직접 colab에서 이것저것 만져본 결과 추측하기로는 다음 표과 같은듯 하다.
코드
설명
p.get_x() + p.get_width()/2.
수치가 들어갈 x축 위지
height+3
y축 위치(현재 +3)
height
수치의 값을 조절할 것인지(현재 +0)
ha = ‘center’
수치를 (x,y)축의 가운데로 정렬
size=9
폰트의 크기이다
여기서 혹시나 ha = 'center'부분이 잘 이해가 안될수 있다.내가그랬다 ha =는 (x,y)축의 기준이 될 곳을 정하는 인자인듯 하다. center말고도 left,right등을 사용할수 있는데 막대의 기준에서 왼쪽,오른쪽이 아닌 텍스트의 기준에서 왼쪽,오른쪽이라 방향을 선택하면 오히려 반대로 배치되는것을 알 수 있다.
표현할 값이 두 개인 막대 그래프
1 2 3 4 5 6 7 8 9
# 기본주석 생략 ax = sns.countplot(x = "day", data = tips, hue = "sex", dodge = True, order = tips['day'].value_counts().index) for p in ax.patches: height = p.get_height() ax.text(p.get_x() + p.get_width()/2., height+3, height, ha = 'center', size=9) ax.set_ylim(-5, 100)
plt.show()
Output
이 코드에서 첫째줄의 인자를 표로 나타내면
코드
설명
x = “day”
x축이 나타낼 자료
data = tips
표현할 데이터셋
hue = “sex”
그래프로 표현할 항목
dodge = True
항목끼리 나눠서 표현할 것인지
order = tips[‘day’].value_counts().index
‘day’의 값이 높은 순서대로 그래프 정렬
sns.countplot() x축이 나타낼 자료, 나타낼 데이터셋, 그래프로 나타낼 항목, 항목끼리 나눠서 표현할것인지, ‘day’의 값이 높은 순서대로 막대그래프 정렬
상관관계 그래프
데이터 불러오기 및 행, 열 갯수 표시하기
1 2 3 4 5 6 7 8 9 10
import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt
mpg = sns.load_dataset("mpg") print(mpg.shape) # 398 행, 9개 열
num_mpg = mpg.select_dtypes(include = np.number) # num_mpg에 'mpg' 데이터셋의 데이터타입 총갯수를 입력한다(숫자형 데이터타입만 포함) print(num_mpg.shape) # 398 행, 7개 열 (두개가 사라진 이유는 number타입이 아닌 Object타입이기 때문)
# 기본 그래프 [Basic Correlation Heatmap] sns.heatmap(num_mpg.corr(), ax=ax[0]) ax[0].set_title('Basic Correlation Heatmap', pad = 12)
# 상관관계 수치 그래프 [Correlation Heatmap with Number] sns.heatmap(num_mpg.corr(), vmin=-1, vmax=1, annot=True, ax=ax[1]) ax[1].set_title('Correlation Heatmap with Number', pad = 12)
plt.show()
Output
위의 코드에서 pad는 히트맵과 타이틀의 간격설정이며, set_title의 인자를 설명하면 (히트맵을 만들 ‘데이터셋.corr()’, 히트맵의 최소값, 최대값, 수치표현(bool값), 마지막인자는 확실하지는 않지만 앞의 히트맵 설정을 어떤 히트맵에 적용시킬지 묻는것 같다.)
상관관계 배열 만들기
1 2 3 4
# import numpy as np # 윗단 코드에서 만들어진 num_mpg 사용 print(int(True)) np.triu(np.ones_like(num_mpg.corr()))
np.triu(배열, k=0)는 위 결과처럼 우하향 대각선이 있고 위 아래로 삼각형이 있다 생각했을때 아래쪽의 삼각형이 모두 0이 되는 함수이다. k의 숫자가 낮아질수록 삼각형은 한칸씩 작아진다. 위 결과에서 행과 열이 7칸이 된 이유는 np.ones_like(num_mpg.corr())의 행이 7개 이기때문인듯 하다. 확실히 모르겠음 질문 필수
이 챕터의 내용은 코드가 너무 긺으로 시각화 결과물을 접지않고 코드를 접는형식으로 서술하겠음.
필수 코드이므로 생략을 생략
1 2 3 4
import matplotlib.pyplot as plt from matplotlib.ticker import (MultipleLocator, AutoMinorLocator, FuncFormatter) import seaborn as sns import numpy as np
import matplotlib.pyplot as plt from matplotlib.ticker import (MultipleLocator, AutoMinorLocator, FuncFormatter) import seaborn as sns import numpy as np
import matplotlib.pyplot as plt from matplotlib.ticker import (MultipleLocator, AutoMinorLocator, FuncFormatter) import seaborn as sns import numpy as np
import matplotlib.pyplot as plt from matplotlib.ticker import (MultipleLocator, AutoMinorLocator, FuncFormatter) import seaborn as sns import numpy as np