*아래 글은 부스트캠프 AI Tech 3기 안수빈 마스터님의 강의를 정리 및 재구성한 내용입니다.
Data Visualization
2-3. Line Plot
1) Line Plot이란?
Line Plot
- 연속적으로 변화하는 값을 순서대로 점으로 나타내고, 이를 선으로 연결한 그래프
- 꺾은선 그래프, 선 그래프, line chart, line graph 등의 이름으로 사용됨
- 시간/순서에 대한 변화에 적합하며 추세를 살피기 위해 사용
- .line()이 아니라 .plot()으로 호출
Line Plot의 요소
- 5개 이하의 선을 한 그래프에 그릴 것을 추천
- 선을 구별하는 요소
- 색상(color)
- 마커(marker, markersize)
- 선의 종류(linestyle, linewidth)
Line Plot 을 위한 전처리
- 시시각각 변하는 데이터는 노이즈로 인해 패턴 및 추세 파악이 어렵다
- Noise의 인지적인 방해를 줄이기 위해 smoothing을 진행하고 그래프를 그리는 경우가 많다
2) 기본 Line Plot 그리기
기본 그리기
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
- plot은 𝑥1,𝑥2,⋯, 𝑦1,𝑦2,⋯ 데이터를 사용해서 그린다
- line plot은 왼쪽에서 오른쪽으로 그리는 게 일반적이지만, 문법 자체는 이전 점 (𝑥1,𝑦1)에서 (𝑥2,𝑦2)로 잇고, (𝑥2,𝑦2)에서 (𝑥3,𝑦3)로 잇는 순차적인 선으로 구성된 그래프이다
fig, axes = plt.subplots(1, 2, figsize=(12, 7))
x1 = [1, 2, 3, 4, 5] # 기본적으로 x축은 정렬
x2 = [1, 3, 2, 4, 5]
y = [1, 3, 2, 1, 5]
axes[0].plot(x1, y)
axes[1].plot(x2, y)
plt.show()

- 좀 더 테크닉을 사용하면 다음과 같이 정N각형이나 원을 그릴 수도 있다
fig = plt.figure(figsize=(5, 5))
ax1 = fig.add_subplot(121, aspect=1)
ax2 = fig.add_subplot(122, aspect=1)
n = 1000
x1 = np.sin(np.linspace(0, 2*np.pi, n))
y1 = np.cos(np.linspace(0, 2*np.pi, n))
x2 = np.sin(np.linspace(0, 2*np.pi, 9))
y2 = np.cos(np.linspace(0, 2*np.pi, 9))
ax1.plot(x1, y1)
ax2.plot(x2, y2)
plt.show()

Plot 변형하기
- 랜덤한 데이터를 사용해서 기본 그래프 그리기
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
np.random.seed(97)
x = np.arange(7)
y = np.random.rand(7)
ax.plot(x, y)
plt.show()

- 그래프에서 3가지 요소를 변경해보자
- 색(color)
- 마커(marker) : 마커의 종류
- 선의 종류(linestyle) : solid, dashed, dashdot, dotted, None
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
np.random.seed(97)
x = np.arange(7)
y = np.random.rand(7)
ax.plot(x, y,
color='black', # 선 색상을 검정색으로 설정
marker='*', # 마커 변경
linestyle='solid', # 선을 실선으로 설정
)
plt.show()

3) 시계열 데이터를 이용해 Line Plot 그리기
Line Plot을 위한 데이터 전처리
- 시계열 데이터 사용 : 미국 주식 데이터
stock = pd.read_csv('./prices.csv')
stock
stock['date'] = pd.to_datetime(stock['date'], format='%Y-%m-%d', errors='raise')
stock.set_index("date", inplace = True)
stock

- apple과 google의 주식 데이터만 가져오자
# FAANG
apple = stock[stock['symbol']=='AAPL']
google = stock[stock['symbol']=='GOOGL']
google.head()

- 데이터를 전처리하지 않고 바로 line plot을 그리면 다음과 같다
fig, ax = plt.subplots(1, 1, figsize=(15, 7), dpi=300)
ax.plot(google.index, google['close'])
ax.plot(apple.index, apple['close'])
plt.show()

- moving average(이동 평균)를 사용하여 google 주식 데이터를 smoothing 한 뒤 그래프를 비교하면 다음과 같다.
google_rolling = google.rolling(window=20).mean() # moving average(이동 평균)를 이용한 smoothing, window 20
fig, axes = plt.subplots(2, 1, figsize=(12, 7), dpi=300, sharex=True)
axes[0].plot(google.index,google['close'])
axes[1].plot(google_rolling.index,google_rolling['close'])
plt.show()

4) Line Plot Tips
정확한 Line Plot 그리기
- line plot은 추세에 집중하기
- Bar plot과 달리 꼭 축을 0에 초점을 둘 필요는 없음 (추세를 보기 위한 목적이므로)
- 너무 구체적인 line plot보다는 생략된 line plot이 추세를 파악하기에 더 나을 수 있다
- Grid, annotation 등 제거, 디테일한 정보는 표로 제공하는 것을 추천
- 데이터가 너무 생략되지 않는 선에서 범위를 조정하여 변화율을 관찰할 수도 있다(.set_ylim())
from matplotlib.ticker import MultipleLocator
fig = plt.figure(figsize=(12, 5))
np.random.seed(970725)
x = np.arange(20)
y = np.random.rand(20)
# Ax1
ax1 = fig.add_subplot(121)
ax1.plot(x, y,
marker='o',
linewidth=2)
ax1.xaxis.set_major_locator(MultipleLocator(1))
ax1.yaxis.set_major_locator(MultipleLocator(0.05))
ax1.grid(linewidth=0.3)
# Ax2
ax2 = fig.add_subplot(122)
ax2.plot(x, y,
linewidth=2,)
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)
ax1.set_title(f"Line Plot (information)", loc='left', fontsize=12, va= 'bottom', fontweight='semibold')
ax2.set_title(f"Line Plot (clean)", loc='left', fontsize=12, va= 'bottom', fontweight='semibold')
plt.show()

간격으로 인한 오해 해결하기
- 규칙적인 간격이 아니라면 데이터의 오해를 줄 수 있다
- 아래 세 그래프를 비교해보자
x = [2, 3, 5, 7, 9]
y = [2, 3, 4, 7, 10]
fig, ax = plt.subplots(1, 3, figsize=(13, 4))
ax[0].plot([str(i) for i in x], y)
ax[1].plot(x, y)
ax[2].plot(x, y, marker='o')
plt.show()

- 위 그래프를 그릴 때 데이터는 x = 2, 3, 5, 7, 9에 대해 존재한다.
- 첫번째 그래프는 x축의 2~3의 간격과 나머지 눈금들의 간격의 실제 값의 차가 다르다. 따라서 기울기 정보에 대한 오해가 생길 수 있다.(실제로는 2~3사이의 기울기 값이 3~5사이의 기울기 값보다 크다)
- 두번째 그래프는 x축의 간격은 잘 설정이 되었지만 실제로는 데이터가 없는 x = 4, 6, 8에 데이터가 있는 것처럼 보일 수 있다.
- 따라서 x = 2, 3, 5, 7, 9와 같이 규칙적인 간격의 데이터가 아닌 경우에는 세번째 그림과 같이 x축 간격은 일정하게 유지하고 관측값에 점으로 마커를 표시하여 오해를 방지해야 한다.
보간하기
- 보간 : 점과 점 사이에 데이터가 없는 경우 이를 잇는 방법
- 데이터에 error나 noise가 있는 경우, moving average나 smooth curve with scipy를 통해 데이터를 전처리할 수 있다.
- 보간은 보여주기에는 좋게 만들 수 있으나, 없는 데이터를 있다고 오해하게 만들고 눈여겨 봐야하는 미세한 차이를 없애는 등 데이터를 왜곡할 위험이 있으므로 굳이 보간이 필요하지 않다면 일반적인 분석에서는 지양하도록 하자
from scipy.interpolate import make_interp_spline, interp1d # scipy 라이브러리에서 보간 함수 import
import matplotlib.dates as dates
fig, ax = plt.subplots(1, 2, figsize=(20, 7), dpi=300)
date_np = google.index
value_np = google['close']
date_num = dates.date2num(date_np)
# smooth
date_num_smooth = np.linspace(date_num.min(), date_num.max(), 50)
spl = make_interp_spline(date_num, value_np, k=3)
value_np_smooth = spl(date_num_smooth)
# print
ax[0].plot(date_np, value_np)
ax[1].plot(dates.num2date(date_num_smooth), value_np_smooth)
plt.show()

이중 축 사용
- 한 plot에 대해 2개의 축을 사용하는 것을 말한다
- 같은 시간 축에 대해 서로 다른 종류의 데이터를 표현하기 위해서는 축이 2개가 필요하다
- .twinx()를 사용하여 구현 가능하다
- 주로 한 데이터에 대해 두 개의 다른 단위가 있을 때 사용한다(ex. radian 과 degree)
- 사실 두 데이터를 반드시 한 공간에 그려야 하는 게 아니라면 이중 축을 사용하기 보다 두 개의 plot을 그리는 것이 훨씬 낫다
- 이중 축 사용은 두 데이터가 상관관계가 있는 게 아니라면 웬만하면 지양하자
fig, ax1 = plt.subplots(figsize=(12, 7), dpi=150)
# First Plot
color = 'royalblue'
ax1.plot(google.index, google['close'], color=color)
ax1.set_xlabel('date')
ax1.set_ylabel('close price', color=color)
ax1.tick_params(axis='y', labelcolor=color)
# # Second Plot
ax2 = ax1.twinx()
color = 'tomato'
ax2.plot(google.index, google['volume'], color=color)
ax2.set_ylabel('volume', color=color)
ax2.tick_params(axis='y', labelcolor=color)
ax1.set_title('Google Close Price & Volume', loc='left', fontsize=15)
plt.show()

* 참고 : secondary-xaxis(), secondary-yaxis()
Secondary Axis — Matplotlib 3.5.1 documentation
Sometimes we want a secondary axis on a plot, for instance to convert radians to degrees on the same plot. We can do this by making a child axes with only one axis visible via axes.Axes.secondary_xaxis and axes.Axes.secondary_yaxis. This secondary axis can
matplotlib.org
def deg2rad(x):
return x * np.pi / 180
def rad2deg(x):
return x * 180 / np.pi
fig, ax = plt.subplots()
x = np.arange(0, 360)
y = np.sin(2 * x * np.pi / 180)
ax.plot(x, y)
ax.set_xlabel('angle [degrees]')
ax.set_ylabel('signal')
ax.set_title('Sine wave')
secax = ax.secondary_xaxis('top', functions=(deg2rad, rad2deg))
secax.set_xlabel('angle [rad]')
plt.show()

Line Plot에서 범례 표시하기
- Line Plot에서는 라인 끝 단에 레이블을 추가하면 그래프를 식별하는 데 도움을 줄 수 있다
일반 범례 사용 시
fig = plt.figure(figsize=(12, 5))
x = np.linspace(0, 2*np.pi, 1000)
y1 = np.sin(x)
y2 = np.cos(x)
ax = fig.add_subplot(111, aspect=1)
ax.plot(x, y1,
color='#1ABDE9',
linewidth=2, label='sin')
ax.plot(x, y2,
color='#F36E8E',
linewidth=2, label='cos')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# 일반 범례 사용
ax.legend(loc='upper center')
plt.show()

그래프 끝에 표시 하는 경우
fig = plt.figure(figsize=(12, 5))
x = np.linspace(0, 2*np.pi, 1000)
y1 = np.sin(x)
y2 = np.cos(x)
# Ax2
ax = fig.add_subplot(111, aspect=1)
ax.plot(x, y1,
color='#1ABDE9',
linewidth=2,)
ax.plot(x, y2,
color='#F36E8E',
linewidth=2,)
ax.text(x[-1]+0.1, y1[-1], s='sin', fontweight='bold',
va='center', ha='left',
bbox=dict(boxstyle='round,pad=0.3', fc='#1ABDE9', ec='black', alpha=0.3))
ax.text(x[-1]+0.1, y2[-1], s='cos', fontweight='bold',
va='center', ha='left',
bbox=dict(boxstyle='round,pad=0.3', fc='#F36E8E', ec='black', alpha=0.3))
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.show()

Line Plot에서 Min/Max 또는 원하는 정보에 포인트 주기
fig = plt.figure(figsize=(7, 7))
np.random.seed(97)
x = np.arange(20)
y = np.random.rand(20)
ax = fig.add_subplot(111)
ax.plot(x, y,
color='lightgray',
linewidth=2,)
ax.set_xlim(-1, 21)
# max
ax.plot([-1, x[np.argmax(y)]], [np.max(y)]*2,
linestyle='--', color='tomato'
)
ax.scatter(x[np.argmax(y)], np.max(y),
c='tomato',s=50, zorder=20)
# min
ax.plot([-1, x[np.argmin(y)]], [np.min(y)]*2,
linestyle='--', color='royalblue'
)
ax.scatter(x[np.argmin(y)], np.min(y),
c='royalblue',s=50, zorder=20)
plt.show()

'부스트캠프 AI Tech 공부 기록 > Data Visualization' 카테고리의 다른 글
| [Data Viz] Text (feat.Matplotlib) (0) | 2022.02.06 |
|---|---|
| [Data Viz] Matplotlib 사용법 : Scatter Plot (0) | 2022.02.05 |
| [Data Viz] Matplotlib 사용법 : Bar Plot (0) | 2022.02.04 |
| [Data Viz] Matplotlib 사용법 : 기본 (0) | 2022.02.03 |
| [Data Viz] 데이터 시각화 개요 (0) | 2022.02.03 |