- tight_layout()으로 axes 사이 간격을 적절하게 조정할 수 있습니다.
- subplots, legend와 함께 사용하는 방법을 알아봅시다.
- 간혹 tight_layout()이 잘 안될 때 해결하는 방법도 알아봅시다.
Contributor
데이터짱님, 안수빈님
1. 최종적으로 그릴 그림
-
이번 글에서, 우리는 이 그림을 그릴겁니다.

-
포인트는 다음과 같습니다.
- subplot과 눈금 숫자가 겹치지 않게 간격 벌리기
- 범례를 한쪽에 모으기
- 화면에 보이는 대로, 잘리는 부분 없이 파일에 출력하기
- 경고(Warning) 메시지 보이지 않게 하기
2. 예제 그림 만들기
- 예제로 사용할 그림을 먼저 만듭니다.
- 이번 글에서는 비슷한 그림이 여러번 반복됩니다.
- 자칫 지겨울 수 있으니 색상을 랜덤으로 지정합시다.
1 | import matplotlib.pyplot as plt |

-
ax.legend()를 사용해서 각 axes마다 범례를 그립니다. -
원이 들어간 axes 사이의 간격은 적절해 보입니다.
- 문제는 눈금에 붙은 숫자가 다른 axes를 침범하고 있는 것입니다.
- 이 문제를 해결해 보겠습니다.
-
fig.savefig()는 그림을 파일로 저장하는 코드입니다.- 글을 읽으시는 데 불필요하다고 판단하여 이제까지 대부분 이 줄을 생략했습니다.
- 주피터 노트북에서는 시각화 출력물이 노트북상에 보이기 때문인데,
이번 글에서 화면에 보이는 그림과 저장되는 파일이 다른 경우를 보여드리겠습니다.
-
fig.set_facecolor()는 figure 색상 지정 명령입니다.- axes가 차지하는 공간을 보여주려고 색을 칠했습니다.
3. 범례 모으기
axes.legend()는 각 axes에 범례를 붙입니다.- 전체를 한번에 모으려면
fig.legend()를 사용합니다.
1 | fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(6, 4)) |

4. axis 사이 간격 벌리기
fig.tight_layout()한 줄을 추가해줌으로써 간격을 적당히 벌릴 수 있습니다.
1 | fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(6, 4)) |

-
fig.tight_layout()은 axes의 크기를 조정합니다.- 기준은 ticklabels, axis labels, title입니다.
-
바꾸어 말하면, legend나 suptitle은 고려의 대상이 아닙니다.
- tight_layout으로 인해 그 바람에 axes가 찌그러졌습니다.
- 데이터에 따라 관계가 없을 수도 있지만, 저는 원을 그리고 싶었습니다.
- 데이터 모양을 다시 살려봅니다.
5. axes aspect ratio 맞추기
- axes의 가로세로 비율을 데이터 스케일에 일치시킵니다.
ax.set_aspect("equal")을 사용합니다.
1 | fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(6, 4)) |

6. legend 옆으로 빼기
-
하나로 모인 legend가 axes 하나를 다 차지하고 있네요.
-
옆으로 빼겠습니다.
-
legend 이동은
loc와bbox_to_anchor두 인자의 조합으로 이루어집니다.- legend에서 loc가 가리키는 곳을
- 여기서는 “upper left” = 왼쪽 위 귀퉁이
- figure의 어떤 지점에 갖다 붙이라는 의미입니다.
- bbox_to_anchor=(1,0.95) = 오른쪽 위 끄트머리
- legend에서 loc가 가리키는 곳을
-
적용해 보겠습니다.
1 | fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(6, 4)) |
-
화면에 잘 나옵니다.

-
그런데 저장된 파일은 이렇습니다.

-
범례의 위치를 bbox_to_anchor=(1,0.95)라고 지정한 것을 상기합시다.
- x 좌표가 figure의 끝자락인 1인데 loc가 upper left로 정의되어 있습니다.
- 다시 말해 figure 오른쪽 경계 너머에 붙이라는 의미라서 안 보이는 게 정상입니다.
- 따라서, 범례까지 저장하려면 그림의 전체 범위를 figure 안쪽으로 잡아야 합니다.
7. 전체 그림 범위 지정하기
-
fig.tight_layout()의 rect 인자를 조정합니다. -
figure 안에 가상의 사각형을 그리고 여기에만 axes를 배열하는 기능입니다.
- rect=[x시작, y시작, x길이, y길이]로 넣어줍니다.
- axes 범위를 조정한 만큼, legend의
bbox_to_anchor도 조정합니다.
-
적용합시다.
1 | fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(6, 4)) |

- 화면 출력과 파일 출력이 다시 일치합니다.
- 그런데 그림이 작아졌습니다.
- tight_layout()으로 그림 사이 간격이 넓어지고
- rect 옵션으로 영역 좁아지고
- set_aspect()로 그 안에서 또 종횡비를 맞춘 결과입니다.
8. 그림 크기 재조정
- 그림 크기를 다시 조정해 줍니다.
- 가로를 늘려도 되고, 세로를 줄여도 됩니다.
1 | # figsize를 1 늘려줍니다. |

9. fig.add_axes()방식 워터마크 삽입
- 지난 글에서 워터마크를 넣는 여러 방식을 알아봤습니다.
- 이 중 fig.add_axes()방식은 아무데나 원하는 크기로 넣을 수 있어서 워터마크 외에도 응용하기 좋습니다.
- 그런데 오와 열이 맞지 않기 때문에 tight_layout() 입장에서는 처리하기 곤란한 대상입니다.
1 | fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(7, 4)) |

- 다행히 파일 저장이 무사히 되었습니다.
- 이번엔 운이 좋았지만 종종 그림이 밀려 어긋나거나 사라집니다.
10. tight_layout()보다 더 강한 constrained_layout
-
아까 tight_layout은 ticklabels, axis labels, title를 본다고 했습니다.
-
constrained_layout은 legend, colorbar까지 함께 봅니다.
- matplotlib 내부 연산을 거쳐 딜레이가 있습니다.
- 따라서 real-time visualization에는 적합치 않을 수 있습니다.
-
두 가지 중 한 가지 방식으로 사용할 수 있습니다.
(1) 그림별 설정
지금 만드는 그림에 constrained_layout을 설정합니다.
1 | fig, ax = plt.subplots(constrained_layout=True) |
(2) 기본값 설정
지금 이후 만드는 모든 그림에 설정합니다.
1 | plt.rcParams['figure.constrained_layout.use'] = True |
- tight_layout 대신 constrained_layout을 적용합니다.
1 | fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(7, 4), |
-
화면에 잘 나옵니다.

-
그런데 저장된 파일은 또 이렇습니다.

-
당연한 결과입니다.
- constrained_layout은 tight_layout의 rect같은 인자가 없기 때문입니다.
- 하지만, 부분적으로나마 더 좋은 기능이 있습니다.
10. figure 대신 axes에 범례 달기
-
우리가 만난 문제를 정리해봅시다.
(1) axes 바깥쪽에 범례를 달고 싶음.
(2) 그런데 figure의 범위가 제한되어 있음.
(3) constrained_layout은 웬만한 axes 구성 요소에 맞춰줌
(4) constrained_layout은 figure 구성 요소를 축소하지 못함 -
어려워 보이지만 해결책이 숨어 있습니다.
- constrained_layout은 figure 범례엔 못 맞춥니다.
- 하지만 ax가 만드는 범례에는 맞춰줍니다
- 따라서, 범례를 axes에 달면 됩니다.
-
코드를 바꿉니다.
-
axes들이 만드는 도형을
handles에 모으고, label을labels에 모읍니다. -
오른쪽 위에 있는 axes[0, 2]에 legend를 답니다.
1 | fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(7, 4), |

- handles와 labels를 따로 만들어 사용하는 점이 복잡해 보이지만,
- 상당한 matplotlib 유발 감정을 해소할 수 있는 기능입니다.
- 조금은 익숙해져 봅시다. :)