Contributor
현재웅님
- matplotlib은 데이터를 그림으로 보여주지만 그림을 읽어 보여주기도 합니다.
- 이 성질을 이용하면 다양한 연출을 할 수 있습니다.
- 대용량 데이터의 시각화 결과물을 그림파일로 저장하고 재활용해봅시다.
1. 대용량 데이터 다루기
- Matplotlib은 큰 데이터를 다루기에 적합한 도구는 아닐 지도 모릅니다.
- 커다란 데이터를 읽으라면 DOS 공격으로 오인하기도 합니다.
- 이럴 때 택할 수 있는 방법 하나는 데이터를 조금씩 읽어서 그리는 것입니다.
1.1. 대용량 데이터 만들기
- skew가 있는 정규분포 데이터 1억개 X 10 쌍을 만듭니다.
- skew가 있으면 정규분포의 중심축이 가운데서 옆으로 밀린 모양이 됩니다.
1
2
3
4
5
6
7
8
9
10import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.stats import skewnorm
sns.set_style("whitegrid")
sns.set_context("talk")
for i in range(10):
d = skewnorm.rvs(np.random.randint(20), size=100000000)
np.save(f"./data_{i}.npy", d, allow_pickle=True)
1.2. 대용량 데이터 그리기
- 대용량 데이터를 다루는 방법 중 하나는 한번에 한 덩어리씩 그리는 것입니다.
- 1억개 데이터를 한 세트씩 읽고, 그리고, 버립니다.
- 본 예제에서는 boxplot을 그려봅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20fig, axs = plt.subplots(ncols=10, sharey=True, figsize=(10,5))
for i, ax in enumerate(axs):
# 데이터 한 세트 읽기
data_small = np.load(f"./data_{i}.npy", allow_pickle=True)
# 데이터 한 세트 그리기
sns.boxplot(y=data_small, ax=axs[i], orient="v", color="orange")
ax.set_xlabel(f"X{i}")
ax.grid(b=None)
if i != 0:
ax.spines["left"].set_visible(False)
if i != 9:
ax.spines["right"].set_visible(False)
# 그린 데이터 세트 버리기
del data_small
fig.tight_layout()
fig.savefig("plot0.png")
- 여러 subplots에 나누어 그렸지만 마치 하나에 있는 것처럼 보이게 하고자 합니다.
spines[].set_visible(False)
를 이용해서 subplots 사이의 spines를 모두 보이지 않게 처리했습니다.- 언뜻 봐서는 한 axes에 있는 것 같지만 위 아래 spine이 끊겨 있습니다.
- 이 틈을 메워봅시다.
2. 끊긴 spine 이어주기
2.1. 그리자마자 이어주기
- 가장 좋은 방법은 그림을 그릴 때 메우는 것입니다.
- 그림을 그린 직후, 커다란 axes를 그리고 spine만 남기고 모두 지웁니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26fig, axs = plt.subplots(ncols=10, sharey=True, figsize=(10,5))
for i, ax in enumerate(axs):
# 데이터 한 세트 읽기
data_small = np.load(f"./data_{i}.npy", allow_pickle=True)
# 데이터 한 세트 그리기
sns.boxplot(y=data_small, ax=axs[i], orient="v", color="orange")
ax.set_xlabel(f"X{i}")
ax.grid(b=None)
if i != 0:
ax.spines["left"].set_visible(False)
if i != 9:
ax.spines["right"].set_visible(False)
# 그린 데이터 세트 버리기
del data_small
# 커다란 테두리 만들어 주기
ax_big = fig.add_subplot(111)
ax_big.grid(b=None)
ax_big.set_facecolor("None")
ax_big.set_xticks([])
ax_big.set_yticks([])
fig.tight_layout()
- 그런데 아차, 깜빡하고 테두리를 잇지 않고 저장했다면 어떻게 해야 할까요?
- 제 예제의 경우 그림을 그리는 데만 약 4분이 걸립니다. 데이터가 많을수록 더 오래 걸릴 것입니다.
- 다행히 테두리가 끊긴 그림파일을 저장해 두었습니다. 이 것을 이용해 봅시다.
2.2. 그린 다음에 이어주기
- matplotlib의 이미지 처리 기능을 이용합니다.
- 그림을
imread()
로 읽어와서imshow()
로 띄운 뒤 끊어진 선 위에 새로운 선을 올려놓으면 됩니다.
2.1. ax.imread()
- 반사적으로 figure와 axes를 만들고 읽어봅니다.
- 손가락이 알아서
fig, ax = plt.subplots()
를 입력하고 있습니다.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15fig, ax = plt.subplots(figsize=(10,5))
# 이미지 읽어오기
im = plt.imread("./plot0.png")
# axes에 이미지 출력
ax.imshow(im)
# 이미지 빼고 나머지 요소들 지우기
ax.grid(b=None)
ax.set_facecolor("None")
ax.set_xticks([])
ax.set_yticks([])
fig.tight_layout()
- 뭔가 이상합니다.
- axes안에 이미지가 들어가 버려서 좋지 않습니다.
- figure와 axes 사이 여백때문에 그림의 여백이 추가됩니다.
- figure size를 원래 그림과 맞춰주어도 살짝 그림이 눌리면서 해상도가 나빠집니다.
2.2. fig.figimage()
- axes가 아니라 figure에 이미지를 담는 것으로 해결할 수 있습니다.
fig.figimage()
명령을 사용합니다.- 그리고 아까처럼 테두리 제작용으로 커다란 axes를 만들어 줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20fig, ax = plt.subplots(figsize=(10, 5))
# 이미지 읽어오기
im = plt.imread("./43_toomany_1.png")
# figure에 이미지 출력
fig.figimage(im, resize=False)
# axes 안보이게 하기
ax.set_xticks([])
ax.set_yticks([])
ax.axis("off")
# 테두리 제작용 커다란 axes 만들기
ax_big = fig.add_subplot(111)
ax_big.set_xticks([])
ax_big.set_yticks([])
fig.tight_layout()
fig.savefig("plot1.png")
- 주피터 노트북에 보이는 도형들이 줄을 제대로 맞추지 않고 있습니다.
- 그러나 걱정하지 않아도 됩니다. 저장된 그림 파일을 열어보면 정상적으로 줄이 맞아 있습니다.
- axes 바깥쪽을 건드릴 때 종종 주피터 노트북에 보이는 결과와 파일로 저장되는 경우가 다릅니다.
- 파일로 저장한 결과를 사용한다면, 가끔 저장된 파일을 열어볼 필요가 있습니다.
2.3. 위에 다른 그림 그리기
- 끊긴 테두리를 이어주기 위해서
ax_big
을 만들었습니다. - 그런데 생각해보면 이것도 하나의 axes입니다.
- 이 위에 다른 데이터를 그림으로 그리거나, 다양한 도형을
patches
로 추가할 수 있습니다.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27fig, ax = plt.subplots(figsize=(10, 5))
im = plt.imread("./43_toomany_1.png")
fig.figimage(im, resize=False)
ax.set_xticks([])
ax.set_yticks([])
ax.axis("off")
ax_big = fig.add_subplot(111, zorder=2)
ax_big.set_xticks([])
ax_big.set_yticks([])
ax_big.set_facecolor("none")
ax_big.spines["left"].set_visible(False)
ax_big.spines["right"].set_visible(False)
ax_big.spines["top"].set_bounds(0.1, 1)
ax_big.spines["bottom"].set_bounds(0.1, 1)
ax_big.spines["bottom"].set_position(("outward", -34))
X = np.linspace(0, 1, 51)
c = np.random.random(size=51)
ax_big.scatter(X, np.sin(10*X), c=c, s=300*c**2, cmap="jet", alpha=0.3)
ax_big.set_ylim(-1.5, 1.5)
fig.tight_layout()
ax_big
공간에 사인 그래프로 장난을 쳐 봤습니다.- 본인의 용도에 따라 마음껏 활용하시면 됩니다.