[시각화] Matplotlib pyplot을 활용한 데이터 시각화 2
업데이트:
개요
이번 포스팅에서는 시계열 데이터를 활용해서 선을 활용한 Line plot을 시각화 해보고자 한다. 뿐만 아니라 축의 설정이나 여러 데이터를 한번에 시각화하는 등 세부적인 시각화 스킬들에 대해 소개해보고자 한다(디테일하고 사소한 설정이지만 시행착오를 겪었던 부분들이라 메모도 할겸..).
기본 설정에 대한 정보들은 이전 포스팅을 참고하면 좋을 것 같다.
Line Plot
선으로 그래프를 표시할때는 주로 연속성이 존재하는, 시계열 데이터를 표현할때 주로 활용한다. 이를 위한 예제 데이터로 기상청 날씨 데이터를 활용해보자.
아래 데이터는 관측지점(지점)별, 시간(일시)별 다양한 기상정보를 담고 있는 공공데이터이다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.read_csv("https://raw.githubusercontent.com/yganalyst/data_example/main/weather.csv", encoding='cp949')
df["일시"] = pd.to_datetime(df["일시"])
df.head(5)
지점 | 일시 | 기온(°C) | 기온 QC플래그 | 강수량(mm) | 강수량 QC플래그 | 풍속(m/s) | 풍속 QC플래그 | 풍향(16방위) | 풍향 QC플래그 | ... | 최저운고(100m ) | 시정(10m) | 지면상태(지면상태코드) | 현상번호(국내식) | 지면온도(°C) | 지면온도 QC플래그 | 5cm 지중온도(°C) | 10cm 지중온도(°C) | 20cm 지중온도(°C) | 30cm 지중온도(°C) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 184 | 2019-09-01 00:00:00 | 23.7 | NaN | NaN | NaN | 2.0 | NaN | 180.0 | NaN | ... | 35.0 | 2000.0 | NaN | NaN | 23.1 | NaN | 26.3 | 25.7 | 25.2 | 26.9 |
1 | 184 | 2019-09-01 01:00:00 | 23.7 | NaN | NaN | NaN | 2.1 | NaN | 180.0 | NaN | ... | 73.0 | 2000.0 | NaN | NaN | 23.0 | NaN | 26.0 | 25.5 | 25.0 | 26.9 |
2 | 184 | 2019-09-01 02:00:00 | 23.5 | NaN | NaN | NaN | 1.4 | NaN | 180.0 | NaN | ... | 69.0 | 2000.0 | NaN | NaN | 22.9 | NaN | 25.8 | 25.3 | 24.9 | 26.9 |
3 | 184 | 2019-09-01 03:00:00 | 23.4 | NaN | NaN | NaN | 1.1 | NaN | 180.0 | NaN | ... | 10.0 | 2000.0 | NaN | NaN | 22.6 | NaN | 25.6 | 25.2 | 24.7 | 27.0 |
4 | 184 | 2019-09-01 04:00:00 | 23.4 | NaN | NaN | NaN | 1.6 | NaN | 180.0 | NaN | ... | 10.0 | 2000.0 | NaN | NaN | 22.6 | NaN | 25.5 | 24.9 | 24.5 | 27.0 |
5 rows × 36 columns
산점도 그릴 때와 마찬가지로 plt.plot()
함수를 활용하면 되는데, 단순히 마커만 표시하는게 아니라 간단하게 선으로 연결해주기만 하면 된다.
spot_ = 184
d_ = df[(df["지점"]==spot_) & (df["일시"].dt.month==9)]
plt.figure(figsize=(10,5))
plt.title("9월 %s 지점 기온 추이" % spot_, fontsize=15)
plt.plot(d_["일시"], d_["기온(°C)"], "-", color='grey', label=str(spot_))
plt.grid()
plt.legend(fontsize=13)
plt.xticks(rotation=90)
plt.show()
여러 데이터 중첩해서 그리기
여러 데이터를 중첩해서 그리기 위해서는 그래프 창 설정(plt.figure()
)과 최종(plt.show()
)사이에 여러번 plot을 해주기만 하면 된다.
color의 경우 직접 넣어주거나 아에 지정하지 않으면 default로 분류해준다. 이는 Line plot말고도 다른 모든 시각화방법에서도 동일하게 적용된다.
plt.figure(figsize=(10,5))
plt.title("9월 지점별 기온 추이", fontsize=15)
for spot_ in [184, 185, 188, 189]:
d_ = df[(df["지점"]==spot_) & (df["일시"].dt.month==9)]
plt.plot(d_["일시"], d_["기온(°C)"], "-", label=str(spot_), alpha=.6)
plt.grid()
plt.legend(fontsize=13)
plt.xticks(rotation=90)
plt.show()
시간대별로 시각화(표기)하기
위에서 데이터를 보면 일별데이터가 아닌 시간대별 데이터임을 알 수 있다. 그런데 이렇게 날짜데이터를 시각화할때 default설정과 달리 원하는 방식으로 날짜표기를 하고 싶을 수 있다.
그럴때는 matplotlib.dates
를 활용해서 날짜형식을 formatting할 수 있고, 예를들어 하루동안의 기상변화를 “시간:분:초”단위로 표시하고 싶다면 다음과 같이하면 된다.
import matplotlib.dates as mdates
spot_ = 184
d_ = df[(df["지점"]==spot_) & (df["일시"]<"2019-09-02")]
plt.figure(figsize=(10,5))
plt.title("시간대별 기온 추이(2019년 9월 1일)", fontsize=15)
ax = plt.subplot(1,1,1)
ax.plot(d_["일시"],d_["기온(°C)"], "-o", color='red', label=str(spot_))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
plt.legend(fontsize=13)
plt.grid()
plt.show()
수평, 수직선 그리기
데이터에서 평균값에 대한 척도를 수평(or 수직)으로 같이 보여주고 싶을때가 있다. plt.axvline
(수직)과 plt.axhline
(수평)을 활용할 수 있다.
spot_ = 184
d_ = df[(df["지점"]==spot_) & (df["일시"].dt.month==9)]
plt.figure(figsize=(10,5))
plt.title("9월 %s 지점 기온 추이" % spot_, fontsize=15)
plt.plot(d_["일시"], d_["기온(°C)"], "-", color='grey', label=str(spot_))
plt.axhline(y= d_["기온(°C)"].mean(), linestyle='--', color='red', label="mean")
plt.axvline(x= pd.to_datetime("2019-09-15"), color='blue', label="spot")
plt.grid()
plt.legend(fontsize=13)
plt.xticks(rotation=90)
plt.show()
y축 2개 활용하기
두개의 데이터를 동시에 시각화하고 싶은데, 단위가 달라서 적절하게 보여지지 않을 경우 하나의 y축을 우측에도 사용함으로써 보여줄 수 있다.
이를 위해서 twinx()
을 통해 동일한 그래프에 두 축을 활용할 수 있고, 두 축에대한 범례를 get.label()
함수를 통해 개별적으로 추가해주어야 한다.
spot_ = 184
d_ = df[(df["지점"]==spot_) & (df["일시"].dt.month==9)]
plt.figure(figsize=(10,5))
plt.title("9월 %s 지점 기온,풍속 추이" % spot_, fontsize=15)
plt.xticks(rotation=90)
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
a, = ax1.plot(d_["일시"], d_["기온(°C)"], "-", color='red', label="기온(°C)")
b, = ax2.plot(d_["일시"], d_["풍속(m/s)"], "-", color='blue', label="풍속(m/s)")
p = [a, b]
ax1.legend(p, [p_.get_label() for p_ in p])
plt.show()
Pandas에 내장된 기능을 활용하면 아래와 같이 보다 간편한 방법으로 시각화 할 수 있다.
ax = d_.plot('일시','기온(°C)', figsize=(10,5))
plt.title("9월 %s 지점 기온,풍속 추이" % spot_, fontsize=15)
d_.plot('일시','풍속(m/s)',secondary_y=True, ax=ax)
plt.show()
한 화면에 여러 창의 그래프 그리기
지금까지는 한번에 1개의 그래프를 그렸었다. 하지만 한 그래프 창(Figure)을 분할해서 여러개의 그래프 창들(Subplots)을 생성해 동시에 다수의 그래프들을 시각화 하는 방법을 소개한다.
앞서도 몇번 나왔지만, 기본적으로 matplotlib
에서는 Figure와 axes이라는 개념이 존재한다.
위 그림과 같이 Figure는 전체 그래프 화면을 의미하며, 이 안에 그래프 창인 Axes가 존재한다. 기본적으로 Figure는 Axes를 포함하고 있기 때문에 한번에 하나의 그래프를 그릴때는 따로 Axes를 건드릴 필요가 없다. 그러나 위에서 잠깐 나왔던 것처럼 창의 세부적인 설정(축 등)을 위해서는 Axes객체를 다뤄주어야 하고, 지금과 같이 한 화면에 여러 창을 그리기 위해서는 한 Figure 객체 내에 여러개의 Axes를 만들면 된다.
plt.subpplot(행 개수, 열 개수, 해당 그래프의 위치)
로 사용할 수 있다.
plt.figure(figsize=(10,8)) # 먼저 창을 만들고
n=1
for spot_ in [184, 185, 188, 189]:
d_ = df[(df["지점"]==spot_)]
ax = plt.subplot(2,2,n) # for문을 돌면서 Axes를 추가
plt.title("%s 지점 기온 추이" % spot_, fontsize=15)
ax.plot(d_["일시"], d_["기온(°C)"], "-", label=str(spot_), alpha=.6) # 그래프 추가
plt.xticks(rotation=90)
n+=1
plt.tight_layout() # 창 크기에 맞게 조정
plt.show()
여기서 창을 여러개 그리다보면 축이 겹치기도 하고 잘 안맞는 경우가 생긴다.
이럴때 plt.tight_layout()
을 사용해서 창 크기에 맞게 Axes를 조절해주는 작업이 필요하다.
범례 위치 조정하기
그래프에서 범례는 꼭 표시해야하는 기능인데, 이 범례 때문에 그래프가 가려지거나 하는 이유로 위치를 조절해줘야할 필요가 있다.
앞서 사용했었던 plt.legend(loc='위치 명칭(또는 Code)')
로 조절이 가능하다. 앞에서 지점별로 중첩해서 그렸던 자료를 활용해보자.
Location String | Location Code |
---|---|
‘best’ | 0 |
‘upper right’ | 1 |
‘upper left’ | 2 |
‘lower left’ | 3 |
‘lower right’ | 4 |
‘right’ | 5 |
‘center left’ | 6 |
‘center right’ | 7 |
‘lower center’ | 8 |
‘upper center’ | 9 |
‘center’ | 10 |
plt.figure(figsize=(10,5))
plt.title("9월 지점별 기온 추이", fontsize=15)
for spot_ in [184, 185, 188, 189]:
d_ = df[(df["지점"]==spot_) & (df["일시"].dt.month==9)]
plt.plot(d_["일시"], d_["기온(°C)"], "-", label=str(spot_), alpha=.6)
plt.grid()
plt.legend(loc='lower left', fontsize=13)
plt.xticks(rotation=90)
plt.show()
위 파라미터들로 Axes내부에서 자유롭게 이동이 가능하지만, 아에 범례를 그래프 밖으로 빼고 싶거나 세부적으로 조절하고 싶을때는
bbox_to_anchor
과 borderaxespad
를 활용해서 세부조정이 가능하다.
plt.figure(figsize=(10,5))
plt.title("9월 지점별 기온 추이", fontsize=15)
for spot_ in [184, 185, 188, 189]:
d_ = df[(df["지점"]==spot_) & (df["일시"].dt.month==9)]
plt.plot(d_["일시"], d_["기온(°C)"], "-", label=str(spot_), alpha=.6)
plt.grid()
plt.legend(loc='upper right', bbox_to_anchor=(1.13, 1), borderaxespad=0, fontsize=13)
plt.xticks(rotation=90)
plt.show()
grid 가로 또는 세로만 표시하기
지금까지 계속 사용했었던 plt.grid()
는 xtick과 ytick에 맞춰 격자를 생성한다. 하지만 이게 때로는 지저분해 보일때도 있고, 가로(또는 세로)만 필요할때가 있다.
이럴때는 다음과 같이 plt.gca()
및 xaxis.grid()
function을 활용할 수 있다.
spot_ = 184
d_ = df[(df["지점"]==spot_) & (df["일시"].dt.month==9)]
plt.figure(figsize=(10,5))
plt.title("9월 %s 지점 기온 추이" % spot_, fontsize=15)
plt.plot(d_["일시"], d_["기온(°C)"], "-", color='grey', label=str(spot_))
ax = plt.gca()
ax.xaxis.grid(True)
plt.legend(fontsize=13)
plt.xticks(rotation=90)
plt.show()
Heat map
히트맵은 어떤 행과 열의 Matrix에 존재하는 값들을 효과적으로 시각화하는데 유용하게 사용된다. 데이터 분석에서는 주로 상관계수(Correalation)를 그릴때 사용한다. 이는 만들어진 매트릭스를 뿌려주기만 하면 되므로 개인적으로 seaborn
라이브러리를 활용하는 편이다.
heat_ = df[["기온(°C)","풍속(m/s)","습도(%)","현지기압(hPa)","해면기압(hPa)","지면온도(°C)"]].dropna().copy()
import seaborn as sns
plt.figure(figsize=(8,8))
plt.title("Correlation", fontsize=15)
sns.heatmap(data = heat_.corr(),
annot=True,
fmt = '.2f', linewidths=.5, cmap='coolwarm',
vmin = -1, vmax = 1, center = 0)
plt.show()
Correlation에 대한 이론적인 부분은 추후에 따로 포스팅할 예정이다.
댓글남기기