다중 서브플롯

때로는 데이터의 다양한 관점을 나란히 비교하는 것이 도움이 될 수 있습니다. 이를 위해 Matplotlib에는 서브플롯, 즉 단일 그림 내에서 함께 존재할 수 있는 작은 축 그룹이라는 개념이 있습니다. 이러한 하위 플롯은 삽입, 플롯 그리드 또는 기타 더 복잡한 레이아웃일 수 있습니다. 이번 장에서는 Matplotlib에서 서브플롯을 생성하는 네 가지 루틴을 살펴보겠습니다. 사용할 패키지를 가져오는 것부터 시작하겠습니다.

import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
plt.style.use('seaborn-white')

plt.axes: 직접 서브플롯

축을 생성하는 가장 기본적인 방법은 plt.axes 함수를 사용하는 것입니다. 이전에 살펴본 것처럼 이는 전체 그림을 채우는 표준 축 객체를 생성합니다. plt.axes는 또한 그림 좌표계([왼쪽, 아래쪽, 너비, 높이])의 4개 숫자 목록인 선택적 인수를 사용하며, 범위는 그림 왼쪽 하단의 0부터 그림 오른쪽 상단의 1까지입니다.

For example, we might create an inset axes at the top-right corner of another axes by setting the x and y position to 0.65 (that is, starting at 65% of the width and 65% of the height of the figure) and the x and y extents to 0.2 (that is, the size of the axes is 20% of the width and 20% of the height of the figure). The following figure shows the result:

ax1 = plt.axes()  # standard axes
ax2 = plt.axes([0.65, 0.65, 0.2, 0.2])

객체 지향 인터페이스 내에서 이 명령에 해당하는 것은 fig.add_axes입니다. 다음 그림과 같이 이를 사용하여 두 개의 수직으로 쌓인 축을 만들어 보겠습니다.

fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.5, 0.8, 0.4],
                   xticklabels=[], ylim=(-1.2, 1.2))
ax2 = fig.add_axes([0.1, 0.1, 0.8, 0.4],
                   ylim=(-1.2, 1.2))

x = np.linspace(0, 10)
ax1.plot(np.sin(x))
ax2.plot(np.cos(x));

이제 서로 접촉하는 두 개의 축(눈금 레이블이 없는 상단)이 있습니다. 상단 패널의 하단(위치 0.5)이 하단 패널의 상단(위치 0.1 + 0.4)과 일치합니다.

plt.subplot: 서브플롯의 간단한 그리드

정렬된 하위 플롯의 열이나 행은 Matplotlib에 쉽게 생성할 수 있는 여러 편의 루틴이 있을 만큼 충분히 일반적인 요구 사항입니다. 이들 중 가장 낮은 수준은 그리드 내에 단일 하위 플롯을 생성하는 ’plt.subplot’입니다. 보시다시피, 이 명령은 행 수, 열 수, 왼쪽 위에서 오른쪽 아래로 실행되는 플롯의 인덱스 등 세 가지 정수 인수를 사용합니다(다음 그림 참조).

for i in range(1, 7):
    plt.subplot(2, 3, i)
    plt.text(0.5, 0.5, str((2, 3, i)),
             fontsize=18, ha='center')

plt.subplots_adjust 명령을 사용하여 이러한 플롯 사이의 간격을 조정합니다. 다음 코드는 동등한 객체 지향 명령 fig.add_subplot을 사용합니다. 다음 그림은 결과를 보여줍니다.

fig = plt.figure()
fig.subplots_adjust(hspace=0.4, wspace=0.4)
for i in range(1, 7):
    ax = fig.add_subplot(2, 3, i)
    ax.text(0.5, 0.5, str((2, 3, i)),
           fontsize=18, ha='center')

여기서는 서브플롯 크기 단위로 그림의 높이와 너비에 따른 간격을 지정하는 plt.subplots_adjusthspacewspace 인수를 사용했습니다(이 경우 공간은 서브플롯 너비와 높이의 40%입니다).

plt.subplots: 전체 그리드를 한 번에

방금 설명한 접근 방식은 큰 하위 도표 그리드를 생성할 때, 특히 내부 도표에서 x축 및 y축 레이블을 숨기려는 경우 금방 지루해집니다. 이러한 목적으로 plt.subplots를 사용하는 것이 더 쉬운 도구입니다(subplots 끝에 있는 s에 유의하세요). 단일 서브플롯을 생성하는 대신 이 함수는 단일 라인에 서브플롯의 전체 그리드를 생성하여 NumPy 배열로 반환합니다. 인수는 서로 다른 축 간의 관계를 지정할 수 있는 선택적 키워드 sharexsharey와 함께 행 수와 열 수입니다.

동일한 행의 모든 ​​축이 y축 스케일을 공유하고 동일한 열의 모든 축이 x축 스케일을 공유하는 \(2 \times 3\) 서브플롯 그리드를 만들어 보겠습니다(다음 그림 참조).

fig, ax = plt.subplots(2, 3, sharex='col', sharey='row')

‘sharex’ 및 ’sharey’를 지정하면 그리드의 내부 레이블이 자동으로 제거되어 플롯이 더 깔끔해집니다. 축 인스턴스의 결과 그리드는 NumPy 배열 내에서 반환되므로 표준 배열 인덱싱 표기법을 사용하여 원하는 축을 편리하게 지정합니다(다음 그림 참조).

# axes are in a two-dimensional array, indexed by [row, col]
for i in range(2):
    for j in range(3):
        ax[i, j].text(0.5, 0.5, str((i, j)),
                      fontsize=18, ha='center')
fig

plt.subplot과 비교하여 plt.subplots는 파이썬(Python)의 기존 0 기반 인덱싱과 더 일치하는 반면 plt.subplot은 MATLAB 스타일 1 기반 인덱싱을 사용합니다.

plt.GridSpec: 더 복잡한 배열

일반 그리드를 넘어 여러 행과 열에 걸쳐 있는 하위 도표로 이동하려면 ’plt.GridSpec’이 최고의 도구입니다. plt.GridSpec은 자체적으로 플롯을 생성하지 않습니다. 오히려 plt.subplot 명령으로 인식되는 편리한 인터페이스입니다. 예를 들어 너비와 높이 공간이 지정된 2개의 행과 3개의 열로 구성된 그리드에 대한 GridSpec은 다음과 같습니다.

grid = plt.GridSpec(2, 3, wspace=0.4, hspace=0.3)

여기에서 익숙한 파이썬(Python) 슬라이싱 구문을 사용하여 하위 플롯 위치와 범위를 지정합니다(다음 그림 참조).

plt.subplot(grid[0, 0])
plt.subplot(grid[0, 1:])
plt.subplot(grid[1, :2])
plt.subplot(grid[1, 2]);

이러한 유형의 유연한 그리드 정렬은 다양한 용도로 사용됩니다. 저는 다음 그림과 같은 다축 히스토그램 플롯을 생성할 때 이 방법을 가장 자주 사용합니다.

# Create some normally distributed data
mean = [0, 0]
cov = [[1, 1], [1, 2]]
rng = np.random.default_rng(1701)
x, y = rng.multivariate_normal(mean, cov, 3000).T

# Set up the axes with GridSpec
fig = plt.figure(figsize=(6, 6))
grid = plt.GridSpec(4, 4, hspace=0.2, wspace=0.2)
main_ax = fig.add_subplot(grid[:-1, 1:])
y_hist = fig.add_subplot(grid[:-1, 0], xticklabels=[], sharey=main_ax)
x_hist = fig.add_subplot(grid[-1, 1:], yticklabels=[], sharex=main_ax)

# Scatter points on the main axes
main_ax.plot(x, y, 'ok', markersize=3, alpha=0.2)

# Histogram on the attached axes
x_hist.hist(x, 40, histtype='stepfilled',
            orientation='vertical', color='gray')
x_hist.invert_yaxis()

y_hist.hist(y, 40, histtype='stepfilled',
            orientation='horizontal', color='gray')
y_hist.invert_xaxis()

여백과 함께 플롯되는 이러한 유형의 분포는 Seaborn 패키지에 자체 플롯 API가 있을 정도로 일반적입니다. 자세한 내용은 Seaborn을 사용한 시각화를 참조하세요.