Python/Data Visualization with Python

파이썬 y축 중간 생략 그래프 (broken yaxis) 그리기 - 공부하는 도비

DOVISH WISDOM 2022. 10. 20. 17:19  
728x90
반응형

y축 중간 생략 그래프는 그래프를 그릴 때 각 항목들 간 데이터 크기 차이가 많이 날 때 쓰는 방식으로

이 방법을 사용하면, 완성된 그래프의 퀄리티도 좋아지고 확실히 눈에 잘 들어오게 됩니다. 

 

아래 그래프는 같은 데이터를 사용하여 두 가지 버전의 그래프를 그린 결과입니다. 

y축의 scale이 log 임에도 불구하고, A의 항목이 다른 항목에 비해 데이터 크기가 매우 크다는 걸 알 수 있습니다.

만약에 scale이 log가 아니라면, B와 C 항목은 보이지도 않습니다. ㅎㅎ 

 

 

파이썬에서 y축 중간 생략 그래프를 그리기 위해선, subplot을 활용합니다.

즉, 두가지 그래프에 각각 그래프를 그려두고, 각 subplot의 ylim을 조정하면서 마치 중간그래프가 생략된 거처럼 만드는 거죠! 

아래 코드에서는 subpot 1에 y축의 범위를 [95, 10000]로 두고, subplot 2에서는 y축의 범위를 [0, 0.1]로 둬서 중간 범위들이 결과 그래프 상에선 보이지 않게 되는 겁니다.  

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle

# dataset
data = pd.DataFrame({
    "Number of items encrypted" : ['2^10','2^10','2^10', '2^11','2^11','2^11','2^12', '2^12','2^12', '2^13', '2^13', '2^13'],
    "Algorithms" : ["A", "B", "C", "A", "B", "C", "A","B", "C", "A","B", "C"],
    "Encryption time (Sec)" : [150.15, 0.004, 0.005, 230.38, 0.014, 0.016, 900, 0.038, 0.044, 2812, 0.082, 0.088]
})

# setting figure
sns.set(rc={"figure.figsize":(8,6)})
plt.rcParams['lines.linewidth'] = 4.0
plt.rcParams['boxplot.flierprops.markersize'] = 10
sns.set_style("white")

# subplot
f, (ax1, ax2) = plt.subplots(ncols=1, nrows=2, sharex=True)

# making yaxis grid
ax1.yaxis.grid()
ax2.yaxis.grid()

#making barplot for each subplot ax1 and ax2
ax1 = sns.barplot(x = 'Number of items encrypted', y= "Encryption time (Sec)", hue = "Algorithms", data = data,  palette = ["dimgrey", "lightgray", "darkgrey"], ax=ax1)
ax2 = sns.barplot(x = 'Number of items encrypted', y= "Encryption time (Sec)", hue = "Algorithms", data = data,  palette = ["dimgrey", "lightgray", "darkgrey"], ax=ax2)

plt.xticks(size = 16)
plt.yticks(size = 16)

ax1.set_yscale("log")
ax2.set_yscale("log")

ax1.set_ylim(95, 10000)
ax2.set_ylim(0, 0.1)

ax1.set_yticks([100, 1000, 10000])
ax2.set_yticks([0.001, 0.01, 0.1])

ax1.set_ylabel("")
ax2.set_ylabel("")

ax1.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)

f.text(0.02, 0.50, "Encryption time (sec)", va='center', rotation = 'vertical', fontsize = 16)
ax1.get_xaxis().set_visible(False)

ax1.get_legend().remove()
ax2.get_legend().remove()


labels = ax1.set_yticklabels(['$10^2$', '$10^3$', '$10^4$'], fontsize = 16)
labels = ax2.set_yticklabels(['$10^{-3}$','$10^{-2}$', '$10^{-1}$'], fontsize = 16)

# how big to make the diagonal lines in axes coordinates
d = .7    
kwargs = dict(marker=[(-1, -d), (1, d)], markersize=15, linestyle="none", color='k', clip_on=False)

ax1.plot([0, 1], [0, 0], transform=ax1.transAxes, **kwargs)
ax2.plot([0, 1], [1, 1], transform=ax2.transAxes, **kwargs)


labels = ax2.set_xticklabels(['$2^{10}$', '$2^{11}$', '$2^{12}$', '$2^{13}$'], fontsize = "16")

ax1.set_xlabel("")

ax1.legend(title = "Encryption time", handles=[Rectangle((0,0), 0, 0, color = "dimgrey",alpha = 1, label="A algorithm"), Rectangle((0,0), 0, 0, color = "lightgray", alpha = 1, label="B algorithm"), Rectangle((0,0), 0, 0, color = "darkgrey", alpha = 1, label="C algorithm")], loc = "lower left", bbox_to_anchor=(0, 0.40, 1, 0), ncol = 1, fontsize = "medium", title_fontsize = "medium")

ax2.set_xlabel("Number of items encrypted", fontsize = "16")

plt.savefig("result.pdf")

결과 그래프

 

과정을 더 자세히 살펴보면, 아래 코드를 실행시키면 빗금이 포함된 subplot 1, 2가 생성됩니다. 

import pandas as pd
import matplotlib.pyplot as plt

f, (ax1, ax2) = plt.subplots(ncols=1, nrows=2, sharex=True)
d = .7    # how big to make the diagonal lines in axes coordinates

kwargs = dict(marker=[(-1, -d), (1, d)], markersize=15, linestyle="none", color='k', clip_on=False)

ax1.plot([0, 1], [0, 0], transform=ax1.transAxes, **kwargs)
ax2.plot([0, 1], [1, 1], transform=ax2.transAxes, **kwargs)

plt.show()

ax1 그래프의 x축 아래 선과 ax2 그래프의 x축 위 선을 안 보이도록 코드를 추가해줍니다. 

ax1.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)

너무 밋밋하니 y축에 grid를 넣어볼게요.

ax1.yaxis.grid()
ax2.yaxis.grid()

위에서 봤던 예제처럼, ylim을 조정하고 yticks을 설정해줍니다. 그럼 중간 생략 그래프 완성! 

ax1.set_ylim(79, 100)
ax2.set_ylim(0, 20)

ax1.set_yticks([80, 85, 90, 95, 100])
ax2.set_yticks([0, 5, 10, 15, 20])

 

마지막은 저 빗금도 크기와 색, 스타일을 바꿀 수 있다는 걸 보여드리고 이번 피드는 마무리하겠습니다. 

import pandas as pd
import matplotlib.pyplot as plt

f, (ax1, ax2) = plt.subplots(ncols=1, nrows=2, sharex=True)
d = .8    # how big to make the diagonal lines in axes coordinates

kwargs = dict(marker=[(-1, -d), (1, d)], markersize=30, linestyle="none", color='r', clip_on=False)

ax1.plot([0, 1], [0, 0], transform=ax1.transAxes, **kwargs)
ax2.plot([0, 1], [1, 1], transform=ax2.transAxes, **kwargs)

ax1.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)

ax1.yaxis.grid()
ax2.yaxis.grid()

ax1.set_ylim(79, 100)
ax2.set_ylim(0, 20)

ax1.set_yticks([80, 85, 90, 95, 100])
ax2.set_yticks([0, 5, 10, 15, 20])

plt.show()

반응형