호기심 많은 분석가
[BoostCamp] Data_Viz_1. Line Plot & Scatter Plot 본문
(2-2) Line Plot 사용하기
기본 Line Plot
1. Line plot이란?
- Line Plot은 연속적으로 변화하는 값을 순서대로 점으로 나타내고, 이를 선으로 연결한 그래프
- 꺾은선 그래프, 선 그래프, line chart, line graph 등의 이름으로 사용됨
- 시간/순서에 대한 변화에 적합하여 추세를 살피기 위해 사용
- 시계열 분석에 특화!!
- 신기하게 .line이 아니라
.plot()
2. Line plot의 요소
- 5개 이하의 선을 사용하는 것을 추천
- 더 많은 선은 중첩으로 인한 가독성 하락
- 그렇다면 이를 구별하는 요소는 어떤 것이 있을까?
- 색상 (
color
) - 마커 (
marker
,markersize
) - 선의 종류 (
linestyle
,linewidth
)solid
,dashed
,dashdot
,dotted
,None
- 색상 (
3. Line plot을 위한 전처리
- 시시각각 변동하는 데이터(Ex) 주식)는 Noise로 인해 패턴 및 추세 파악이 어려움
- Noise의 인지적인 방해를 줄이기 위해 smoothing 사용
# Tip, to_datetime에 error를 처리해주는 parameter가 존재
pd.to_datetime(stock['date'], format='%Y-%m-%d', errors='raise')
# 비율 고정, 아래의 경우 1대 1 사각형 생성
fig.add_subplot(111, aspect=1)
# random.seed(1996), 랜덤 값 고정
np.random.seed(1996)
np.random.rand(7) # 항상 같은 랜덤 값이 나온다
# rolling 문법을 통해 moving average 구해줌, window의 숫자만큼 앞에 있는 데이터와
# mean(평균)을 취해준다.
google_rolling = google.rolling(window=20).mean()
# dpi : 고해상도 지정, default=100, sharex : x축 고정
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()
정확한 Line Plot
- 오용을 줄여보자
1. 추세에 집중
- Bar plot과 다르게 꼭 축을 0에 초점을 둘 필요는 없다
- 추세를 보기 위한 목적이므로
- 너무 구체적인 line plot보다는 생략된 line plot이 더 나을 수 있다.
- Grid, Annotate 등 모두 제거
- 디테일한 정보를 표로 제공하는 것을 추천
- 생략되지 않는 선에서 범위를 조정하여 변화율 관찰 (
.set_ylim()
)
from matplotlib.ticker import MultipleLocator
# 축 간격을 조정해주는 명령어
ax1.xaxis.set_major_locator(MultipleLocator(1))
ax1.yaxis.set_major_locator(MultipleLocator(0.05))
ax1.grid(linewidth=0.3)
# 1) verticalalignment (va)
# - center : 수직방향으로 좌표 중앙에 놓임
# - top : 좌표가 텍스트 위에 놓임
# - bottom : 좌표가 텍스트 아래 놓임
# - baseline : 텍스트의 baseline 에 따라 달라짐
# 2) horizontal alignment (ha)
# - center : 수평방향으로 좌표 중앙에 놓임
# - left : 좌표가 텍스트 왼쪽에 놓임
# - right : 좌표가 텍스트 오른쪽에 놓임
ax1.set_title(f"Line Plot (information)", loc='left', fontsize=12, va= 'bottom', fontweight='semibold')
- 하지만 항상 오른쪽이 옳지는 않다. 정확한 값이 필요할 경우 왼쪽 그래프를 쓸 때도 있고, 몇 년간의 추세를 보기 위해서는 오른쪽 그래프를 쓰기도 한다. → 목적이 중요
2. 간격
우리는 기울기로 그래프를 많이 이해하기 때문에 x축의 간격이 굉장히 중요하다
- 규칙적인 간격이 아니라면 오해를 줄 수 있다
- 그래프 상에서 규칙적일 때 : 없는 데이터에 대해 있다고 오해
- 그래프 상에서 간격이 다를 때 : 기울기 정보의 오해
- 규칙적인 간격의 데이터가 아니라면 각 관측 값에 점으로 표시하여 오해를 줄이자.
3. 보간
- Line은 점을 이어 만드는 요소 → 점과 점 사이에 데이터가 없기에 이를 잇는 방법(보간)
- 데이터의 error나 noise가 포함되어 있는 경우, 데이터의 이해를 돕는 방법
- Moving Average
- Smooth Curve with Scipy
scipy.interpolate.make_interp_spline()
scipy.interpolate.interp1d()
scipy.ndimage.gaussian_filter1d()
- Presentation에는 좋은 방법일 수 있으나
- 없는 데이터를 있다고 생각하게 할 수 있으며
- 작은 차이를 없앨 수 있음
- 일반적인 분석에서는 지양할 것!
from scipy.interpolate import make_interp_spline, interp1d import matplotlib.dates as dates date_np = google.index value_np = google['close'] # Date -> Num 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)
- 4. 이중 축 사용
- 한 plot에 대해 2개의 축을 이중 축 (dual axis)라고 함
- 같은 시간 축에 대해 서로 다른 종류의 데이터를 표현하기 위해서는 축이 2개가 필요
.twinx()
를 사용
- 한 데이터에 대해 다른 단위 (ex. radian과 degree)
[.secondary_xaxis()](https://matplotlib.org/stable/gallery/subplots_axes_and_figures/secondary_axis.html)
,.secondary_yaxis()
사용
- 2개의 plot을 그리는 것 >>>>> 이중 축 사용
- 이충 축은 지양할 것, 상관관계가 없는 데 있어보일 수 있기 때문
# 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)
한 번에 2개를 그리는 것보다 따로 그리는 게 가독성이 좋다.
def deg2rad(x):
return x * np.pi / 180
def rad2deg(x):
return x * 180 / np.pi
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]')
5. ETC
- 라인 끝 단에 레이블을 추가하면 식별에 도움 (범례 대신)
x = np.linspace(0, 2*np.pi, 1000)
y1 = np.sin(x)
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))
- Min/Max 정보(또는 원하는 포인트)는 추가해주면 도움이 될 수 있음 (annotation, )
- 두 그래프 중 하나만 주는 것보다 같이 주는 것이 트렌트 파악에 더 도움이 된다
ax.set_xlim(-1, 21)
# max
# -1부터 최댓값의 지점(np.argmax(y))(좌표)까지 plot을 그려줌
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)
- 보다 연한 색을 사용하여 uncertatinty 표현 가능 (신뢰구간, 분산 등)
- Bar Plot에서는 errorbar를 통해 표현했는데, line plot에서는 Area Plot으로 표현함
(2-3) Scatter Plot 사용하기
- 이전의 두 강의에서는 점, 선, 면 중에서 선과 면을 사용했다. 이번에는 마지막 점을 사용한 그래프를 사용할 것
기본 Scatter Plot
1. Scatter plot이란?
- Scatter plot은 점을 사용하여 두 feature간의 관계를 알기 위해 사용하는 그래프
- 산점도 등의 이름으로 사용됨
- 직교 좌표계에서 x축/y축에 feature 값을 매핑해서 사용
.scatter()
를 사용
2. Scatter plot의 요소
- 점에서 다양한 variation 사용 가능 (2차원 데이터에서 N차원 데이터로 확장 가능)
- Bar plot, line plot과 다르게 2차원 데이터에서 시작한다. 그것을 아래 variation을 통해 차원을 늘려가는 것도 가능 → 점의 요소로 차원을 늘릴 수 있다.
- 이 색, 모양, 크기가 인지적으로 어떤 효과를 보일 지 고민하면서 시각화하자
- 색(
color
) - 모양(
marker
) - 크기(
size
)
ax.scatter(x, y,
s= s, # size, 배열로 전달해서 각각의 사이즈를 다르게 할 수 있음
c='white',
marker='o',
linewidth=1,
edgecolor='black')
3. Scatter plot의 목적
- 상관 관계 확인 (양의 상관관계 / 음의 상관관계 / 없음)
- Positive Correlation, Negative Correlation의 경우 실제 수치가 있다.
- 피어슨 계수, 공분산 등등
- Positive Correlation, Negative Correlation의 경우 실제 수치가 있다.
- 세 가지를 확인하자 - 데이터의 세 가지 특징
- 군집 - 데이터가 어떤 식으로 묶여있는가, Ex. KNN, DBSCAN, t-SNE
- 값 사이의 차이
- 이상치 - 신뢰구간 95% 이상, 등등
정확한 Scatter Plot
Scatter plot의 오용을 막아보자
1. Overplotting
실제 데이터의 개수만큼 점으로 표현된다.
- 점이 많아질수록 점의 분포를 파악하기 힘들다!
- 투명도 조정 - 점이 많이 모여있으면 진해지게
- 지터링 (jittering) : 점의 위치를 약간씩 변경, 하지만 다른 방법들이 더 효율적임
- 2차원 히스토그램 : 히트맵을 사용하여 깔끔한 시각화 (x, y축 구간을 나눠서)
- 투명도 조정보다 깔끔하지만, 구체적인 값을 알 수 없기 때문에 같이 사용하는 것을 추천
- Contour plot : 분포를 등고선을 사용하여 표현
2. 점의 요소와 인지
- 색
- 연속은 gradient, 이산은 개별 색상으로
- 마커
- 거의 구별하기 힘들다 + 크기가 고르지 않음
- 잉크 비례의 원칙에 따르면 마커가 표현하는 값이 같으면 크기도 같아야하는데, 동그라미, 네모, 세모 실제 크기와 우리가 인지하는 크기가 다르다 → 조심해야함
- 크기
- 흔히 버블 차트 (bubble chart)라고 부름
- 구별하기는 쉽지만 오용하기 쉬움 (원의 크기 비교)
- 관계보다는 각 점간 비율에 초점을 둔다면 좋음
- SWOT 분석 등에 활용 가능
3. 인과관계와 상관관계
- 인과 관계 (causal relation)과 상관 관계 (correlation)은 다름!
- 분석 과정에서 꼭 고민해볼 것
- 상관 관계는 두 개의 feature가 비슷한 정도가 있어서 같이 커질 수 있다
- 인과 관계는 x축 때문에 y축이 변한다
- 인과 관계는 항상 사전 정보와 함께 가정으로 제시할 것
- 상관 관계가 있다고 항상 인과 관계가 있는 것은 아님
- 상관관계는 추후 heatmap에서 다시 다룰 예정
4. 추세선
- 추세선을 사용하면 scatter의 패턴을 유추할 수 있음
- 단, 추세선이 2개 이상이 되면 가독성이 떨어지므로 주의.
5. ETC
- Grid는 지양, 사용한다면 최소한으로 - Scatter plot과 상성이 안좋음
- 점같이 작은 것들과 겹치면 정보가 오용될 수가 있음
- 색은 무채색으로
- 범주형이 포함된 관계에서는 heatmap 또는 bubble chart를 추천
6. 실습 코드
- Matplotlib dataart를 검색하면 다양한 것들을 배울 수 있다.
- 특정 조건에 따라 색을 다르게 하여 주의를 줄 수 있다.
slc_mean = iris['SepalLengthCm'].mean()
swc_mean = iris['SepalWidthCm'].mean()
ax.scatter(x=iris['SepalLengthCm'],
y=iris['SepalWidthCm'],
c=['royalblue' if yy <= swc_mean else 'gray' for yy in iris['SepalWidthCm']]
)
# scatter_plot을 할 때는 종종 잘리는 부분이 있을 수 있어서 약간씩 조정해준다
ax.set_xlim(0, 4.55)
ax.set_ylim(0, 8.05)
- 위와 같이 반복문과 조건문으로 구분할 수도 있지만 범례를 사용할 예정이므로 세 번 나눠그리는 편이 더 편하다.
for species in iris['Species'].unique():
iris_sub = iris[iris['Species']==species]
ax.scatter(x=iris_sub['PetalLengthCm'],
y=iris_sub['PetalWidthCm'],
label=species)
- 시각적인 주의를 주기 위해서 선을 사용
for species in iris['Species'].unique():
iris_sub = iris[iris['Species']==species]
ax.scatter(x=iris_sub['PetalLengthCm'],
y=iris_sub['PetalWidthCm'],
label=species)
ax.axvline(2.5, color='gray', linestyle=':')
ax.axhline(0.8, color='gray', linestyle=':')
- 좀 더 다양한 관점에서 보기 위해 다음과 같이 그려본다
feat = ['SepalLengthCm', 'SepalWidthCm', 'PetalLengthCm', 'PetalWidthCm']
for i, f1 in enumerate(feat):
for j, f2 in enumerate(feat):
if i <= j :
axes[i][j].set_visible(False)
continue
for species in iris['Species'].unique():
iris_sub = iris[iris['Species']==species]
axes[i][j].scatter(x=iris_sub[f2],
y=iris_sub[f1],
label=species,
alpha=0.7)
if i == 3: axes[i][j].set_xlabel(f2)
if j == 0: axes[i][j].set_ylabel(f1)
# 자동으로 명시된 여백에 관련된 서브플롯 파라미터를 조정
plt.tight_layout()
'Coding > BoostCamp' 카테고리의 다른 글
[BoostCamp] Week3_Day12. 역대 최고의 교육자료, Custom Model (0) | 2021.08.21 |
---|---|
[BoostCamp] Week3_Day11. PyTorch를 배워보자! (0) | 2021.08.17 |
[BoostCamp] Data_Viz_1. 시각화의 기본, Bar Plot (0) | 2021.08.15 |
[BoostCamp] Data_Viz_1. Data Visualization Introduction (0) | 2021.08.15 |
[BoostCamp] Week2_Day10. 딥러닝의 기본을 알다. (0) | 2021.08.15 |