Data Visualization
3-1. Text
1) Text in Data Visualization
시각화에서의 text
- Visualization이 줄 수 없는 많은 설명을 추가해 줄 수 있다
- 잘못된 전달에서 생기는 오해를 방지할 수 있다
- 하지만 text를 과도하게 사용하면 오히려 이해에 방해가 될 수 있다
- Matplotlib에서 제공하는 API 를 바탕으로 Text를 이해하자
Matplotlib에서의 text API
| pyplot API | Object-Oriented API | description |
| suptitle | suptitle | title of figure |
| title | set_title | title of subplot ax |
| xlabel | set_xlabel | x-axis label |
| ylabel | set_ylabel | y-axis label |
| figtext | text | figure text |
| text | text | Axes text |
| annotate | annotate | Axes annotation with arrow |
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
fig.suptitle('Figure Title') # figure의 title
ax.plot([1, 3, 2], label='legend') # 범례 지정
ax.legend()
ax.set_title('Ax Title') # ax title
ax.set_xlabel('X Label') # x label 정보
ax.set_ylabel('Y Label') # y label 정보
ax.text(x=1,y=2, s='Text') # text 좌표 기반으로 지정하기
fig.text(0.5, 0.6, s='Figure Text') # text 비율기반으로 위치 지정하기 (fig의 전체 가로의 0.5, 세로의 0.6 위치에 text 지정)
plt.show()

2) Text Properties
Font Components
- Text의 가독성을 높이기 위해 가장 쉽게 바꿀 수 있는 요소들은 다음과 같다.
- family : 글씨체
- size or fontsize : 글씨 크기
- style or fontstyle : 일반, 기울임 등
- weight or fontweight : 글씨 굵기
- 글씨체에 따른 가독성과 관련된 내용은 다음을 참고하자.
Material Design
Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.
material.io
Is there any research with respect to how font-weight affects readability?
Is there any research with respect to how font-weight affects readability? Obviously, there is some relation there, but I'm struggling to make a decision based on any facts. I created a jsFiddle...
ux.stackexchange.com
- 또한 matplotlib에서 제공하는 Font Demo는 아래 링트에서 볼 수 있다.
Fonts demo (object-oriented style) — Matplotlib 3.5.1 documentation
Note Click here to download the full example code
matplotlib.org
- Font 변형 적용해보기
fig, ax = plt.subplots()
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.text(x=0.5, y=0.5, s='Text\nis Important',
fontsize=20, # fontsize = 'large'도 가능
fontweight='bold',
fontfamily='serif',
)
plt.show()

ETC Details
- 폰트 자체를 변형하는 것은 아니지만 커스텀할 수 있는 요소들은 다음과 같다.
- color : 폰트 색상
- linespacing : 줄과 줄 간의 간격
- backgroundcolor : 폰트 배경색
- alpha : 투명도
- zorder : 피피티에서 맨 앞으로 가져오기 / 맨 뒤로 가져오기 기능
- visible : 폰트 보이게 하기 / 안 보이게 하기
- 적용해보기
fig, ax = plt.subplots()
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.text(x=0.5, y=0.5, s='Text\nis Important',
fontsize=20,
fontweight='bold',
fontfamily='serif',
color='royalblue',
linespacing=2, # 줄과 줄 간의 간격
backgroundcolor='lightgray',
#zorder : 맨앞으로 가져오기 맨 뒤로 가져오기
alpha=0.5
)
plt.show()

Alignment : 정렬
- 정렬과 관련하여 이런 요소들을 조정할 수 있다
- ha : horizontal alignment
- va : vertical alignment
- rotation
- multialignment
- 적용해보기
fig, ax = plt.subplots()
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.text(x=0.5, y=0.5, s='Text\nis Important',
fontsize=20,
fontweight='bold',
fontfamily='serif',
color='royalblue',
linespacing=2,
va='center', # 세로 축 정렬 top : text의 위가 (0.5, 0.5), bottom : text의 아래 좌표가 (0.5, 0.5), center : text의 중앙 좌표가 (0.5, 0.5)
ha='center', # 가로 축 정렬 left, right, center
rotation='horizontal' # vertical? # roatation = 45도 가능
)
plt.show()

Advanced - bbox 그리기
- Drawing Fancy boxes 참고
- 다음과 같이 다양한 박스 스타일이 존재하므로, 적재적소에 사용하면 된다

- 적용해보기
fig, ax = plt.subplots()
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.text(x=0.5, y=0.5, s='Text\nis Important',
fontsize=20,
fontweight='bold',
fontfamily='serif',
color='black',
linespacing=2,
va='center', # top/bottom/center
ha='center', # left/right/center
rotation='horizontal', # vertical로도 바꿔보기
bbox=dict(boxstyle='round', facecolor='wheat', ec = 'blue', alpha=0.4) # ec == edge color
)
plt.show()

3) Text API 별 추가 사용법
Text를 조정 없이 기본적으로 설정하면 다음과 같이 나온다
- 데이터 가져오기
student = pd.read_csv('./StudentsPerformance.csv')
student.head()

- 플롯하기 (Scatter Plot)
fig = plt.figure(figsize=(9, 9))
ax = fig.add_subplot(111, aspect=1)
for g, c in zip(['male', 'female'], ['royalblue', 'tomato']):
student_sub = student[student['gender']==g]
ax.scatter(x=student_sub ['math score'], y=student_sub ['reading score'],
c=c,
alpha=0.5,
label=g)
ax.set_xlim(-3, 102)
ax.set_ylim(-3, 102)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xlabel('Math Score')
ax.set_ylabel('Reading Score')
ax.set_title('Score Relation')
ax.legend()
plt.show()

Title & Legend
- 제목 위치 조정하기
- 범례에 제목, 그림자 달기, 위치 조정하기
fig = plt.figure(figsize=(9, 9))
ax = fig.add_subplot(111, aspect=1)
for g, c in zip(['male', 'female'], ['royalblue', 'tomato']):
student_sub = student[student['gender']==g]
ax.scatter(x=student_sub ['math score'], y=student_sub ['reading score'],
c=c,
alpha=0.5,
label=g)
ax.set_xlim(-3, 102)
ax.set_ylim(-3, 102)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xlabel('Math Score',
fontweight='semibold')
ax.set_ylabel('Reading Score',
fontweight='semibold')
ax.set_title('Score Relation',
loc='left', va='bottom', # 제목 왼쪽 정렬
fontweight='bold', fontsize=15
)
ax.legend(
title='Gender', # 범례 제목
shadow=True,
labelspacing=1.2, # 범례들 간의 공간
loc='lower right', # 범례 위치 lower/upper/center + right/left/center
bbox_to_anchor=[1.2, 0.5], # 범례 위치를 원하는 위치로 지정할 수 있음 -> 권장
#ncol = 2 # 범례 가로로 조정하기, 항목이 엄청 많지 않으면 잘 안 씀
)
plt.show()

* bbox_to_anchor을 더 이해하고 싶다면 → link
What does a 4-element tuple argument for 'bbox_to_anchor' mean in matplotlib?
In the "Legend location" section of the "Legend guide" in the matplotlib website, there's a small script where line 9 is plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3, ncol=2, mode="expand",
stackoverflow.com
Ticks & Text
- tick을 없애거나 조정
- text alignment
* math score 에 대한 bar plot에 적용하기
student['math score'].head()

# bar plot을 만들기 위해 math score 데이터를 범주형으로 변환
def score_band(x):
tmp = (x+9)//10
if tmp <= 1:
return '0 - 10'
return f'{tmp*10-9} - {tmp*10}'
student['math-range'] = student['math score'].apply(score_band)
student['math-range'].value_counts().sort_index()

- 기본 bar plot 그리기
math_grade = student['math-range'].value_counts().sort_index()
fig, ax = plt.subplots(1, 1, figsize=(11, 7))
ax.bar(math_grade.index, math_grade,
width=0.65,
color='royalblue',
linewidth=1,
edgecolor='black'
)
ax.margins(0.07)
# ax.grid() # 꼭 필요할 때만 사용
plt.show()

- ticks frame을 없애고 대신 text 추가
- 제목 추가
math_grade = student['math-range'].value_counts().sort_index()
fig, ax = plt.subplots(1, 1, figsize=(11, 7))
ax.bar(math_grade.index, math_grade,
width=0.65,
color='royalblue',
linewidth=1,
edgecolor='black'
)
ax.margins(0.01, 0.1)
ax.set(frame_on=False) # frame 끄기 - 네 변 자체를 없애기
ax.set_yticks([]) # y축 눈금 제거
ax.set_xticks(np.arange(len(math_grade)))
ax.set_xticklabels(math_grade.index, fontsize=11)
ax.set_title('Math Score Distribution', fontsize=14, fontweight='semibold') # 제목 추가
for idx, val in math_grade.iteritems(): # bar 위에 텍스트 추가
ax.text(x=idx, y=val+3, s=val,
va='bottom', ha='center',
fontsize=11, fontweight='semibold'
)
plt.show()

Annotate : 화살표 사용하기
- 일반 plot ( 특정 학생의 math score 표시 )
fig = plt.figure(figsize=(9, 9))
ax = fig.add_subplot(111, aspect=1)
i = 13
ax.scatter(x=student['math score'], y=student['reading score'],
c='lightgray',
alpha=0.9, zorder=5)
ax.scatter(x=student['math score'][i], y=student['reading score'][i],
c='tomato',
alpha=1, zorder=10)
ax.set_xlim(-3, 102)
ax.set_ylim(-3, 102)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xlabel('Math Score')
ax.set_ylabel('Reading Score')
ax.set_title('Score Relation')
plt.show()

- annotate 추가
- annotate은 text와 가리키려는 대상의 위치를 다르게 지정해줄 수 있음 ( 화살표로 연결 )
fig = plt.figure(figsize=(9, 9))
ax = fig.add_subplot(111, aspect=1)
i = 13
ax.scatter(x=student['math score'], y=student['reading score'],
c='lightgray',
alpha=0.9, zorder=5)
ax.scatter(x=student['math score'][i], y=student['reading score'][i],
c='tomato',
alpha=1, zorder=10)
ax.set_xlim(-3, 102)
ax.set_ylim(-3, 102)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xlabel('Math Score')
ax.set_ylabel('Reading Score')
ax.set_title('Score Relation')
# x축과 평행한 선
ax.plot([-3, student['math score'][i]], [student['reading score'][i]]*2,
color='gray', linestyle='--',
zorder=8)
# y축과 평행한 선
ax.plot([student['math score'][i]]*2, [-3, student['reading score'][i]],
color='gray', linestyle='--',
zorder=8)
# bbox = dict(boxstyle="round", fc='wheat', pad=0.2)
# arrowprops = dict(
# arrowstyle="->")
ax.annotate(s=f'This is #{i} Student', # text = f'This is #{i} Student',
xy=(student['math score'][i], student['reading score'][i]), # 원하는 포인트 잡아주기
xytext=[80, 40], # 텍스트의 위치
bbox=bbox,
arrowprops=arrowprops,
zorder=9,
)
plt.show()

'부스트캠프 AI Tech 공부 기록 > Data Visualization' 카테고리의 다른 글
| [Data Viz] Facet(분할) (feat.Matplotlib) (0) | 2022.02.06 |
|---|---|
| [Data Viz] Matplotlib 사용법 : Scatter Plot (0) | 2022.02.05 |
| [Data Viz] Matplotlib 사용법 : Line Plot (0) | 2022.02.05 |
| [Data Viz] Matplotlib 사용법 : Bar Plot (0) | 2022.02.04 |
| [Data Viz] Matplotlib 사용법 : 기본 (0) | 2022.02.03 |