호기심 많은 분석가
[BoostCamp] Data_Viz_1. 시각화의 기본, Bar Plot 본문
부스트캠프
개발자의 지속 가능한 성장을 위한 학습 커뮤니티
boostcamp.connect.or.kr
Data Visualization의 기본 중의 기본, Bar Plot에 대해 알아보자. 우선 Bar Plot에 앞서 가볍게 Data Check를 할 때 배운 좋은 Method부터 소개하고 시작하겠다.
# 랜덤하게 데이터가 뽑히기 때문에 얻지 못하던 정보를 얻을수도 있음
student.sample(5) # 내가 원하는 갯수의 데이터를 sample로 뽑아줌
# 조금 더 다양하게 describe해줌
student.describe(include='all')
Bar plot이란?
- Bar plot이란 직사각형 막대를 사용하여 데이터의 값을 표현하는 차트/그래프
- 막대그래프, bar chart, bar graph 등의 이름으로 사용됨
- 범주(category)에 따른 수치 값을 비교하기에 적합한 방법
- 개별 비교, 그룹 비교 모두 적합
- 막대의 방향에 따른 분류 (.bar() / .barh())
- 수직 (vertical) : x축에 범주, y축에 값을 표기. (defalut)
- 수평 (horizontal) : y축에 범주, x축에 값을 표기. (범주가 많을 때 적합)
# 막대 그래프의 색은 다음과 같이 변경을 전체로 하거나, 개별로 할 수도 있다
# 개별로 할 때는 막대 개수와 같이 색을 리스트로 전해야 한다.
fig, axes = plt.subplots(1, 2, figsize=(12, 7))
x = list('ABCDE')
y = np.array([1, 2, 3, 4, 5])
clist = ['blue', 'gray', 'gray', 'gray', 'red']
color = 'green'
axes[0].bar(x, y, color=clist)
axes[1].barh(x, y, color=color)
plt.show()
다양한 Bar plot
- 임의의 그룹 데이터를 시각화해보자
- A, B, C, D, E 데이터가 2 그룹에 존재
- Group Sky : [1, 2, 3, 4, 3]
- Group Pink : [4, 3, 2, 5, 1]
- 이 두 데이터를 비교하기 위해서 어떤 방법을 쓸 수 있을까?
- Bar Plot에서는 범주에 대해 각 값을 표현 → 즉 1개의 feature에 대해서만 보여줌
- 여러 Group을 보여주기 위해서는 여러 가지 방법이 필요
- 플롯을 여러 개 그리는 방법
- 한 개의 플롯에 동시에 나타내는 방법
- 쌓아서 표현하는 방법
- 겹쳐서 표현하는 방법
- 이웃에 배치하여 표현하는 방법
1. Multiple Bar Plot
- 각 barplot은 자체적으로 y축 범위를 맞추기에 좀 더 y축의 범위를 공유할 수 있다.
sharey
파라미터 사용fig, axes = plt.subplots(1, 2, figsize=(15, 7), sharey=True) axes[0].bar(group['male'].index, group['male'], color='royalblue') axes[1].bar(group['female'].index, group['female'], color='tomato') plt.show()
sharey로 y축 높이 맞춤 - y축 범위를 개별적으로 조정하는 방법, 이렇게 할 때는 반복문을 사용하여 조정
axes[0].bar(group['male'].index, group['male'], color='royalblue') axes[1].bar(group['female'].index, group['female'], color='tomato') for ax in axes: ax.set_ylim(0, 200)
set_ylim으로 y축 동일
2. Stacked Bar Plot
- 2개 이상의 그룹을 쌓아서(stack) 표현하는 bar plot
- 각 bar에서 나타나는 그룹의 순서는 항상 유지
- 맨 밑의 bar 분포는 파악하기 쉽지만
faxes[0].bar(group_cnt.index, group_cnt, color='darkgray')
axes[1].bar(group['male'].index, group['male'], color='royalblue')
# 위의 행을 주석처리하고 bottom을 써주면 그만큼 비워주게 됨
axes[1].bar(group['female'].index, group['female'], **bottom=group['male']**, color='tomato')
- Stacked Bar Plot이 ABCDE 전체 양에 대해서는 관측에 도움이 되지만, Sky이의 경우 여러 플롯을 그렸을 때와 똑같음, 심지어 레드는 값을 관찰하기 힘들다 → 그래서 이 그래프의 경우 수치를 annotation을 통해 주석(비율 정보)을 달아주는 게 좋다.
- 그래서 조금 응용해서 전체에서 비율을 나타내는 Percentage Stacked Bar Chart가 있음
- 조건부 확률을 알고 싶을 때 유용함
ax.barh(group['male'].index, group['male']/total,
color='royalblue')
ax.barh(group['female'].index, group['female']/total,
left=group['male']/total,
color='tomato')
ax.set_xlim(0, 1)
# graph 테두리 선 제거
for s in ['top', 'bottom', 'left', 'right']:
ax.spines[s].set_visible(False)
3. Overlapped Bar Plot
- 쌓아서 그리니 관측이 어려워서 겹쳐서 그림
- 2개 그룹만 비교한다면 겹쳐서 만드는 것도 하나의 선택지
- 3개 이상에서는 파악이 어렵기 때문
- 같은 축을 사용하니 비교가 쉬움
- 투명도를 조정하여 겹치는 부분 파악 (alpha)
- Bar plot보다는 Area plot에서 더 효과적 → 추후에 다룰 것
for idx, alpha in enumerate([1, 0.7, 0.5, 0.3]):
axes[idx].bar(group['male'].index, group['male'],
color='royalblue',
alpha=alpha)
axes[idx].bar(group['female'].index, group['female'],
color='tomato',
alpha=alpha)
axes[idx].set_title(f'Alpha = {alpha}')
4. Grouped Bar Plot
- 가장 추천하는 방법
- Stacked에 비해 분포를 잘 볼 수 있고, Overlapped에 비해서 가독성이 높다
- 그룹별 범주에 따른 bar를 이웃되게 배치하는 방법
- Matplotlib으로는 비교적 구현이 까다로움
- 적당한 테크닉 (
.set_xticks()
,.set_xticklabels()
)
- 적당한 테크닉 (
- 앞서 소개한 내용 모두 그룹이 5-7개 이하일 때 효과적
- 그룹이 많다면 적은 그룹은 ETC(기타)로 처리
- 크게 3가지 테크닉으로 구현 가능
- x축 조정
width
조정xticks
,xticklabels
- 원래 x축이 0, 1, 2, 3로 시작한다면 - 한 그래프는 0-width/2, 1-width/2, 2-width/2로 구성하면 되고 - 한 그래프는 0+width/2, 1+width/2, 2+width/2로 구성하면 된다.
- 그룹이 N개일 때는 어떻게 할까?
- 그룹의 개수에 따라 x좌표는 다음과 같다.
- 2개 : -1/2, +1/2
- 3개 : -1, 0, +1 (-2/2, 0, +2/2)
- 4개 : -3/2, -1/2, +1/2, +3/2
- 규칙이 보이시나요?
- $-\frac{N-1}{2}에서\frac{N-1}{2}$까지 분자에 2 간격으로 커지는 것이 특징입니다.
- 그렇다면 index i(zero-index)에 대해서는 다음과 같이 x좌표를 계산할 수 있습니다.
- $x+\frac{-N+1+2\times i}{2}\times width$
- 그룹의 개수에 따라 x좌표는 다음과 같다.
x = np.arange(len(group_list))
width=0.12
for idx, g in enumerate(edu_lv):
ax.bar(x+(-len(edu_lv)+1+2*idx)*width/2, group[g],
width=width, label=g)
ax.set_xticks(x)
ax.set_xticklabels(group_list)
ax.legend()
정확한 Bar Plot
1. Principle of Proportion Ink
The representation of numbers, as physically measured on the surface of the graphic itself, should be directly proportion to the numerical quantities represented
- 실제 값과 그에 표현되는 그래픽의 잉크 양은 비례해야 함
- 반드시 x축의 시작은 zero(0)!!
- 만약 차이를 나타내고 싶다면 plot의 세로 비율을 늘리기
- Ex) 첫 번째 막대의 값이 마지막 막대의 2배 이상일까? x축의 시작을 0으로 하지 않음으로써 왼쪽의 Chart가 오용을 불러옴
- 막대그래프에만 한정되는 원칙은 아니다
- Area Plot, Donut Chart 등 다수의 시각화에서 적용
- 좀 더 예쁘다고 좋은 시각화가 아니다!!
2. 데이터 정렬하기
- 더 정확한 정보를 전달하기 위해서는 정렬이 필수
- Pandas에서는
sort_values()
,sort_index()
를 사용해서 정렬
- Pandas에서는
- 데이터의 종류에 따라 다음 기준으로
- 시계열 - 시간순
- 수치형 - 크기순
- 순서형 - 범주의 순서대로
- 명목형 - 범주의 값 따라 정렬
- 여러 가지 기준으로 정렬을 하여 패턴을 발견
- 대시보드에서는 Interactive로 제공하는 것이 유용
3. 적절한 공간 활용
- 여백과 공간만 조정해도 가독성이 높아진다.
- Matplotlib의 bar plot은 ax에 꽉 차서 살짝 답답함
- Matplotlib techniques
- X/Y axis Limit (
.set_xlim(), .set_ylim()
) - Spines (
.spines[spine].set_visible()
) - 트이게 해 줌 - Gap (
width
) - default로 0.8인데, 0.7 정도로 막대 간의 거리를 둬서 가독성 ↑ - Legend (
.legend()
) - Margins (
.margins()
)
- X/Y axis Limit (
ax_basic = fig.add_subplot(1, 2, 1)
ax = fig.add_subplot(1, 2, 2)
ax_basic.bar(group_cnt.index, group_cnt)
ax.bar(group_cnt.index, group_cnt,
width=0.7,
edgecolor='black',
linewidth=2,
color='royalblue'
)
# 그래프 사각형 안에서 margin
ax.margins(0.1, 0.1)
for s in ['top', 'right']:
ax.spines[s].set_visible(False)
4. 복잡함과 단순함
- 필요 없는 복잡함은 NO!!!
- 무의미한 3D는 Never..
- 직사각형이 아닌 다른 형태의 bar는 지양
- 앞의 plot이랑 같은 건데 화려하지만 알아보기 힘듦
3D Visualization
- 무엇을 보고 싶은가? (시각화를 보는 대상이 누구인가?)
- 정확한 차이 (EDA)
- 큰 틀에서 비교 및 추세 파악 (Dashboard)
- 축과 디테일 등의 복잡함
- Grid (
.grid()
) - 격자, 값을 정확히 알려주기 위한 보조도구- 분포나 비율 같은 큰 관점에서 바라볼 때는 지양
- Ticklabels (
.set_ticklabels()
)- Major & Minor
- Text를 어디에 어떻게 추가할 것인가 (
.tex()
or.annotate()
)- Bar의 middle / upper
- Grid (
# 그리드나 텍스트 추가
for ax in axes:
ax.bar(group_cnt.index, group_cnt,
width=0.7,
edgecolor='black',
linewidth=2,
color='royalblue',
zorder=10
)
ax.margins(0.1, 0.1)
for s in ['top', 'right']:
ax.spines[s].set_visible(False)
axes[1].grid(zorder=0)
for idx, value in zip(group_cnt.index, group_cnt):
axes[1].text(idx, value+5, s=value,
ha='center',
fontweight='bold'
)
5. ETC
- 오차 막대를 추가하여 Uncertainty 정볼르 추가 가능 (
errorbar
) - Bar 사이 Gap이 0이라면 → 히스토그램(Histogram)
.hist()
를 사용하여 가능- 연속된 느낌을 줄 수 있음
- 다양한 Text 정보 활용하기
- 제목 (
.set_title()
) - 라벨 (
.set_xlabel()
,.set_ylabel()
)
- 제목 (
score_var = student.groupby('gender').std().T
ax.bar(idx-width/2, score['male'],
color='royalblue',
width=width,
label='Male',
yerr=score_var['male'],
capsize=10
)
ax.bar(idx+width/2, score['female'],
color='tomato',
width=width,
label='Female',
yerr=score_var['female'],
capsize=10
)
ax.set_xticks(idx)
ax.set_xticklabels(score.index)
ax.set_ylim(0, 100)
ax.legend()
ax.set_title('Gender / Score', fontsize=20)
ax.set_xlabel('Subject', fontweight='bold')
ax.set_ylabel('Score', fontweight='bold')
역시 시각화는 아는 만큼 할 수 있다. yerr, ylim, sharey, grid 등 간단하지만 훌륭한 성능을 낼 수 있는 걸 이제 알았다. 얼마전 annotate로 화살표 표시하는 것을 배워 여러 유용한 작업을 할 수 있었는데, 오늘 배운 내용을 토대로도 시각화의 질을 더 높일 수 있겠다. 재밌겠다, 시각화 😍
[AI Tech]Daily Report
Naver AI Tech BoostCamp 2기 캠퍼 허정훈 2021.08.03 - 2021.12.27 https://bit.ly/3oC70G9
www.notion.so
'Coding > BoostCamp' 카테고리의 다른 글
[BoostCamp] Week3_Day11. PyTorch를 배워보자! (0) | 2021.08.17 |
---|---|
[BoostCamp] Data_Viz_1. Line Plot & Scatter Plot (0) | 2021.08.17 |
[BoostCamp] Data_Viz_1. Data Visualization Introduction (0) | 2021.08.15 |
[BoostCamp] Week2_Day10. 딥러닝의 기본을 알다. (0) | 2021.08.15 |
[BoostCamp] Week2_Day9. "Key"word. Transformer (0) | 2021.08.14 |