Matplotlib과 Seaborn 개요
Matplotlib(맷플롯립)
- 파이썬 시각화에 큰 공헌을 한 시각화 라이브러리로 파이썬 세계에서 시각화를 위해서 가장 많이 사용
- 하지만 직관적이지 못한 개발 API로 인해서 시각화 코딩에 익숙해지는 데 많은 시간이 필요하며, 차트의 축 이름이나, 차트 타이틀, 범례(legend) 등의 부가적인 속성까지 코딩을 해주어야 하는 불편함이 있음
Seaborn(시본)
- Matplotlib보다 쉬운 구현, 수려한 시각화, 그리고 판다스와의 연동이 편리한 특징을 가짐
- Matplotlib을 기반으로 하고 있지만, Matplotlib보다 상대적으로 적은 양의 코딩으로도 보다 수려한 시각화 플롯을 제공
- 판다스의 컬럼명을 기반으로 자동으로 축 이름을 설정하는 등 편리한 연동 기능을 가짐
- 하지만 Seaborn 역시 Matplotlib을 기반으로 하고 있으며, 특정 요소들의 경우 Matplotlib 함수들을 그대로 사용하고 있기에 Seaborn을 잘 활용하려면 반드시 Matplotlib을 어느 정도는 알고 있어야 함
Matplotlib은 다양한 그래프/차트를 포함한 많은 시각화 객체(선 그래프, 막대 그래프, 파이 차트 등)와 함수를 제공하지만 여기서는 이들을 설명하지 않을 것이며, 선과 막대 그래프를 이용한 기본 시각화 구현을 실습하면서 Matplotlib을 이용시 개괄적인 시각화 코드 작성 방법에 대해서 설명한다. 본격적인 그래프/차트는 뒤에서 소개하는 Seaborn에서 좀 더 자세히 알아볼 것이다.
Matplotlib
Matplotlib의 pyplot 모듈의 이해
- Matplotlib은 파이썬 시각화를 위한 기반 모듈인 pyplot을 제공하며 이를 통해 시각화를 구현할 수 있음
- title() 함수는 입력값으로 타이틀 문자열을 받아서 차트 타이틀을 설정할 수 있음
- show() 함수를 호출하여 그래프를 화면에 출력할 수 있음
# pyplot 모듈을 관용적으로 plt라는 이름으로 import
import matplotlib.pyplot as plt
# x축 입력값 [1, 2, 3], y축 입력값 [2, 4, 6]으로 선 그래프 생성
plt.plot([1, 2, 3], [2, 4, 6])
# 선그래프 타이틀을 Hello plot으로 설정
plt.title('Hello plot')
# 선 그래프를 출력
plt.show()

pyplot의 두 가지 중요 요소 - Figure와 Axes
Figure
- 그림을 그리기 위한 캔버스의 역할(그림판의 크기를 조절한다든가, 플롯을 최종적으로 화면에 나타내는 역할 수행)
- 하지만 실제적으로 그림을 그리는 역할은 수행하지 않음
Axes
- 실제 그림을 그리는 메서드들을 가짐 → 시각화를 수행하는 메서드들은 Axes에서 호출됨
- X축, Y축, 타이틀, 축 범위 조정, 범례 등의 속성을 설정하는 데도 이용
- 실제 그림을 그리는 데 핵심적인 역할을 수행
→ Axes(축 Axis의 복수형)라는 이름이 주는 인상 때문에 단순히 X축이나 Y축에 관련된 설정 정도의 역할을 수행할 것이라는 선입견과는 다르게 시각화와 관련된 많은 작업들이 이 Axes 객체 기반에서 수행된다. 반면에 Figure는 그림을 그리는 실제 작업에는 크게 연관되어 있지 않다.

앞의 선 그래프를 그리는 예제를 Figure와 Axes 관점에서 설명하면 다음과 같다.
- import matplotlib.pyplot as plt
기본으로 설정된 Figure, Axes가 로딩됨 - plt.plot([1, 2, 3], [2, 4, 6])
기본으로 설정된 Axes에서 Axes.plot() 함수를 호출하여 그림을 그림 - plt.title('Hello plot')
plt.title()은 내부적으로 Axes.set_title() 함수를 호출하여 타이틀을 설정 - plt.show()
plt.show()는 내부적으로 Figure.show()를 호출하여 그림을 나타냄
pyplot의 두 가지 중요 요소 - Figure와 Axes
- plt.figure(figsize=(10,4))와 같이 plt 모듈의 figure() 함수를 호출하면 Figure 객체가 반환된다. 이때 인자로 figsize=(가로크기, 세로크기) 입력하면 해당 크기를 가지는 Figure 객체를 반환하게 된다.
# figure 크기가 가로 10, 세로 4인 Figure 객체를 반환
plt.figure(figsize=(10, 4))
plt.plot([1, 2, 3], [2, 4, 6])
plt.title('Hello plot')
plt.show()

앞서 말했듯이, plt.figure() 함수에 의해 반환된 Figure가 실제로 그림을 그리는 역할은 하지 못하지만 그림판의 배경 색상을 바꾸는 등의 설정을 할 수 있음
plt.figure(figsize=(4, 4), facecolor='yellow')
plt.plot([1, 2, 3], [2, 4, 6])
plt.title('Hello plot')
plt.show()

plt.figure(facecolor='yellow')를 적용해도 전체 그림판이 노란색으로 변하지 않는다. Axes가 차지하는 영역이 Figure가 차지하는 그림판 위에 적용되기 때문이다.
- plt의 axes() 함수는 현재 사용하는 Axes 객체를 반환한다. (참고로 하단의 결과에서 plt.axes()만 호출했는데도 축을 포함한 그림이 표출된 이유는 pyplot 모듈이 import되어 있는 상태에서 주피터노트북은 별도의 plt.show()가 없더라도 Axes가 할당되어 있으면 자동으로 plt.show()와 같은 역할을 하는 함수를 호출하면서 그림이 표출되게 된다.)
ax = plt.axes()

- pyplot에서 Figure와 Axes 객체를 함께 가져올 수 있는데 이는 plt.subplots()을 이용한다. plt.subplots() 함수는 인자로 여러 개의 Axes를 설정할 수 있으며, 여기서는 디폴트 인자만 적용해 단 한개의 Axes만 가져온다.
fig, ax = plt.subplots()

여러 개의 plot을 가지는 subplot들을 생성하기
- plt.subplots() 함수는 하나의 Figure상에서 여러 개의 그래프를 그릴 수 있게 만들어 주는데, 이때 개별 subplot은 하나의 Axes를 가진다. 즉 plt.subplots()은 여러 개의 그래프를 그릴 수 있게 하는 1개의 Figure와 여러 개의 Axes를 생성하고 반환한다.
- plt.subplots()의 주요 인자로 nrows와 ncols, figsize가 사용된다. nrows는 전체 subplot들의 배치를 2차원 행렬 형태로 표현할 때 행의 개수가 되며, ncols는 열의 개수가 된다. 그리고 figsize는 모든 subplot들을 포함한 전체 Figure의 크기를 설정한다.
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(6,3))

plt.subplots(nrows=1, ncols=2, figsize=(6,3)) 수행 시 Figure와 두 개의 Axes 객체를 반환하는데, 이때 반환되는 Axes 객체는 (ax1, ax2)와 같이 튜플 형태로 반환받을 수 있다. ax1이 첫번째 Axes 객체를, ax2가 두번째 Axes 객체를 가리킨다.
하지만 여러 개의 Axes 객체를 튜플 형태가 아니라 넘파이의 ndarray 형태로도 받을 수 있고 이 방법이 더 선호된다. 이는 '여러 개의 subplots들을 이용해 개별 그래프들을 subplot별로 시각화하기' 파트에서 더 알아보자.
pyplot의 plot() 함수를 이용해 선 그래프 그리기
pyplot의 plot() 함수는 선 그래프를 그릴 때 활용된다. X 좌표 값, Y 좌표 값으로 파이썬 리스트, 넘파이 ndarray, 판다스의 DataFrame/Series 모두 적용 가능하다. 다만 입력되는 X 좌표 값과 Y 좌표 값의 개수는 모두 같은 크기를 가져야 한다.
import numpy as np
import pandas as pd
x_value = [1, 2, 3, 4]
y_value = [2, 4, 6, 8]
# x_value = np.array([1, 2, 3, 4])
# y_value = np.array([2, 4, 6, 8])
plt.plot(x_value, y_value)
plt.show()

df = pd.DataFrame({'x_value':[1, 2, 3, 4],
'y_value':[2, 4, 6, 8]})
# 입력값 Series
# plt.plot(df['x_value'], df['y_value'])
# 입력값 DataFrame
plt.plot(df[['x_value']], df[['y_value']])
plt.show()

선 그래프를 보다 다양하게 변화시키고 싶다면 다양한 인자값을 알고 있어야 한다.
<선 그래프를 그릴 때 기본적으로 알고 있으면 편리한 인자값>
- color: 선의 색상
- marker: 마커 형태
- markersize: 마커 size
- linestyle: 선의 형태
- linewidth: 선의 두께
plt.plot(x_value, y_value, color='red', marker='o', markersize=12, linestyle='dashed', linewidth=2)
plt.show()

축 명칭 설정, 축의 눈금(틱)값 회전, 축의 범위, 범례(legend) 설정하기
1) 축 명칭 설정
- plt.xlabel('x축 명칭'): x축 명칭 설정
- plt.ylabel('y축 명칭'): y축 명칭 설정
plt.plot(x_value, y_value, color='red', marker='o', markersize=12, linestyle='dashed', linewidth=2)
plt.xlabel('x axis')
plt.ylabel('y axis')
plt.show()

2) 축의 눈금(틱)값 회전
- plt.xticks(rotation=회전할 각도): x축 눈금값 회전
- plt.yticks(rotation=회전할 각도): y축 눈금값 회전
x_value = np.arange(0, 100)
y_value = 2*x_value
plt.plot(x_value, y_value, color='green')
plt.xlabel('x axis')
plt.ylabel('y axis')
plt.xticks(rotation=90)
plt.show()

X좌표값을 0~99까지 100개로 설정했지만, 0, 20, 40, 60, 80, 100 눈금 값들만 표시가 된다. Matplotlib은 축 값이 문자열인 경우는 모든 값을 나타내 주지만, 축 값이 숫자값이면 자동으로 최솟값과 최댓값에 기반해 축의 크기에 따라 눈금 간격을 만들어 준다.
X축의 눈금 값을 좀 더 세밀하게 나타내고 싶으면 xticks() 함수의 인자로 ticks 값을 나타내고 싶은 값을 설정하면 된다.
x_value = np.arange(0, 100)
y_value = 2*x_value
plt.plot(x_value, y_value, color='green')
plt.xlabel('x_axis')
plt.ylabel('y_axis')
plt.xticks(ticks=np.arange(0, 100, 5), rotation=90)
plt.show()
3) 축의 범위 설정
- plt.xlim(범위값): x축 범위 설정
- plt.ylim(범위값): y축 범위 설정
x_value = np.arange(0, 100)
y_value = 2*x_value
plt.plot(x_value, y_value, color='green')
plt.xlabel('x axis')
plt.ylabel('y axis')
plt.xlim(0, 50)
plt.ylim(0, 100)
plt.show()

4) 범례 설정
범례를 표시하기 위해서는 plt.plot() 함수 내에 label 인자값을 설정해 해당 플롯에 명칭을 부여해 주어야 한다. 그런 다음 plt.legend()를 호출해 범례를 Axes상에 나타낼 수 있다.
- plt.legend() 함수 내 인자값 loc: 지정한 위치에 범례를 나타냄
- plt.legend() 함수 내 인자값 ncol: 범례에 표시될 텍스트의 열의 개수를 지정
x_value_01 = np.arange(0, 100)
y_value_01 = 2*x_value_01
y_value_02 = 4*x_value_01
plt.plot(x_value_01, y_value_01, color='green', label='green line')
plt.plot(x_value_01, y_value_02, color='red', label='red line')
plt.xlabel('x axis')
plt.ylabel('y axis')
plt.legend()
plt.show()

이번에는 선과 막대 그래프를 함께 그려 보도록 한다.
x_value_01 = np.arange(1, 10)
y_value_01 = 2*x_value_01
plt.plot(x_value_01, y_value_01, color='red', marker='o', linestyle='dashed', label='red line')
plt.bar(x_value_01, y_value_01, color='green', label='bar plot')
plt.xlabel('x axis')
plt.ylabel('y axis')
plt.legend()
plt.title('Hello plot')
plt.show()

직접 Axes 객체 메서드를 사용해 시각화 수행
위 예제와 동일한 시각화 결과를 출력하되, Axes 객체에서 직접 작업을 해보자
- plt.axes()를 이용해 Axes 객체를 추출
- plt.plot()은 Axes 객체의 plot(), plt.bar()는 Axes 객체의 bar()로 변경
- 마찬가지로 plt.xlabel(), plt.ylabel()은 각각 Axes 객체의 set_xlabel(), set_ylabel()로 변경
- plt.title()은 Axes 객체의 set_title()로 변경
- 하지만 plt.legend()는 Axes 객체의 set_legend()가 아니라 legend()로 적용되어야 함
추가적으로 plt.xlim(), plt.ylim()도 각각 Axes 객체의 set_xlim(), set_ylim()로 변경
figure = plt.figure()
# Axes 객체를 추출하여 ax 변수에 할당
ax = plt.axes()
# plt.plot()은 ax.plot()으로 plt.bar()는 ax.bar()로 변경
ax.plot(x_value_01, y_value_01, color='red', marker='o', linestyle='dashed', label='red line')
ax.bar(x_value_01, y_value_01, color='green', label='bar plot')
# plt.xlabel()은 ax.set_xlabel()로, plt.ylabel()은 ax.set_ylabel()로 변경
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
# ax.set_legend()가 아니라 ax.legend()
ax.legend()
# plt.title()은 ax.set_title()로 변경
ax.set_title('Hello plot')
plt.show()

여러 개의 subplots들을 이용해 개별 그래프들을 subplot별로 시각화하기
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(6,3))
print(ax)
print(type(ax))
[<Axes: > <Axes: >]
<class 'numpy.ndarray'>

반환된 Axes 객체들을 가지는 ax 변수는 튜플이 아니라 넘파이 ndarray 형태로 가지며 각각 개별 원소인 ax[0]은 왼쪽에 생성된 Axes 객체를, ax[1]은 오른쪽에 생성된 Axes 객체를 가리킨다.
- 여러 개의 subplots들을 이용해서 시각화를 구현할 때는 개별 Axes 객체를 직접 이용해서 시각화를 구현해야 한다.
x_value_01 = np.arange(1, 10)
x_value_02 = np.arange(1, 20)
y_value_01 = 2*x_value_01
y_value_02 = 2*x_value_02
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(6,3))
ax[0].plot(x_value_01, y_value_01, color='red', marker='o', linestyle='dashed', label='red line')
ax[1].bar(x_value_02, y_value_02, color='green', label='bar plot')
ax[0].set_xlabel('ax[0] x axis')
ax[1].set_xlabel('ax[1] x axis')
ax[0].legend()
ax[1].legend()
ax[0].set_title('Hello line')
ax[1].set_title('Hello bar')
plt.show()

- plt.subplots()의 인자인 nrows가 1이거나 또는 ncols가 1인 경우에는 1차원 배열 인덱싱 방식으로 Axes를 접근할 수 있다.(ex: fig, ax = plt.subplots(nrows=1, ncols=3)이라면 ax[0], ax[1], ax[2]와 같은 방식으로 3개의 Axes 객체를 접근할 수 있음)
하지만 nrows도 2 이상이고, ncols도 2 이상이 되면 1차원 배열 인덱싱 방식이 아니라 2차원 배열 인덱싱 방식으로 Axes를 접근해야 한다.
x_value_01 = np.arange(1, 10)
x_value_02 = np.arange(1, 20)
y_value_01 = 2*x_value_01
y_value_02 = 2*x_value_02
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(6,6))
ax[0][0].plot(x_value_01, y_value_01, color='red', marker='o', linestyle='dashed', label='red line')
ax[0][1].plot(x_value_02, y_value_02, color='green', label='green bar')
ax[1][0].plot(x_value_01, y_value_01, color='green', marker='o', linestyle='dashed', label='green line')
ax[1][1].bar(x_value_02, y_value_02, color='red', label='red bar')
ax[0][0].set_xlabel('ax[0][0] x axis')
ax[0][1].set_xlabel('ax[0][1] x axis')
ax[1][0].set_xlabel('ax[1][0] x axis')
ax[1][1].set_xlabel('ax[1][1] x axis')
ax[0][0].legend()
ax[0][1].legend()
ax[1][0].legend()
ax[1][1].legend()
plt.show()
ax[0][0]은 첫번째 로우의 첫번째 컬럼에 해당하는 Axes 객체, ax[0][1]은 첫번째 로우의 두번째 컬럼에 해당하는 Axes 객체, ax[1][0]은 두번째 로우의 첫번째 컬럼에 해당하는 Axes 객체, ax[1][1]은 두번째 로우의 두번째 컬럼에 해당하는 Axes 객체를 가리킨다.
→ 시각화 할 때 인자값 설정 및 내가 원하는 시각화 옵션들을 구글링해서 참고하자
'파이썬 머신러닝 완벽가이드 > [10장] 시각화' 카테고리의 다른 글
| 시각화(2) - Seaborn (0) | 2024.03.26 |
|---|