무효 클릭 IP 추적 중...
머신러닝,딥러닝/matplotlib 시각화

[matplotlib 기초] spine | 축 커스터마이징 방법

꼬예 2021. 6. 1.

안녕하세요 이번시간에는 축의 두께를 조절하고 위치를 조절 하는 등 여러가지 커스터마이징 하는 방법을 알아 보겠습니다. 

 

 

 

 

spines

 

축을 커스터마이징 하는데 사용되는 객체(object)를 spines라고하는데요! 이 녀석을 사용하기전 spines가 어떤 녀석인지 잠시 알아보죠!.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (10,10))

print(type(ax.spines))

print(ax.spines) # key와 value로 구성되어있음

우선 plt.subplots()를 통해 반환된 ax 객체의 property로 spines가 존재합니다.

output :

output을 확인해보면 spines 는 OrderDict 즉 딕셔너리형태로 Key와 value로 구성되어 있습니다. 

Keys 는 보시다시피 left, right, bottom, top 등 위치를 나타낸다는것을 알 수 있구요!

value에는 실제로 우리가 조작할수있는 객체들이 들어가있네요:)

 

우리는 앞으로 이러한 딕셔너리 형태를 이용해서  원하는 커스터마이징을 시작해볼 겁니다. 

 

for 문을 이용하여 원하는 위치만 커스터마이징 하기

본격적으로 다양한 api들을 알아보기전 for문을 이용하는 방법을 알아보겠습니다. 

(물론 for문을 이용하지 않아도 됩니다만.. 다소 DRY(don't repeat yourself) 에 위배 되니 참고바랍니다..)

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (10,10))

for spine_loc, spine in ax.spines.items():
    if spine_loc in ['left', 'bottom']: # 이런식으로 적으면 원하는 위치를 리스트안에 넣어 커스터마이징 가능
        pass
    
    if spine_loc in ['right', 'top']: # 이런식으로 적어주면 하나한 노가다할필 요를 줄일 수 있음
        pass

앞서 말씀드린대로 spines는 딕셔너리 형태입니다. 즉 . items() 를 이용하면 key, value 를 다 불러올수있죠..

위와 같이 spine_loc과 spine에 key와 value를 할당합니다. 

 

spine_loc에 할당된 값은 위치를 나타내는 값임으로 위와 같이 조건문을 이용하여 원하는 위치를 적으면 반복되는 코드를 많이 줄 일 수 있겠죠?

 

 

 

다양한 메소드

1. 축 없애기(spines['원하는위치'].set_visible(False)

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (10,10))

ax.spines['bottom'].set_visible(False)

 

output :

spines의 key값은 위치죠?

즉 원하는 위치값을 [ ] 안에 집어넣으면 해당 위치의 객체가 불러와지고, 원하는 메소드를 넣어 커스터마이징이 가능합니다. 

 

여기선 set_visible이라는 메소드를 사용하였는데요.

보시다시피 set_visible() 안에 False를 넣으면 원하는 위치에 축을 없앨 수 있습니다. 

(눈금 없애는 법은 해당 포스트 참고바랍니다.)

 

2. 축 위치변경(spines['원하는위치'].set_position('center')

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (10,10))

ax.spines['left'].set_position('center') # 왼쪽 위치조정 가운데로 해보기

output :

spines의 key값을 'left'를 가운데(center)로 이동시키는 코드입니다. 충분히 이해가능하시죠?

 

3. 축 두께 변경(spines['원하는위치'].set_linewidth(숫자)

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (10,10))

ax.spines['left'].set_linewidth(2)

output :

 

4. 축 투명도 변경(spines['원하는위치'].set_alpha(숫자)

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (10,10))

ax.spines['left'].set_alpha(0.5)

output :

 

5. 축 컬러 변경(spines['원하는위치'].set_color('컬러')

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (10,10))

ax.spines['left'].set_color('navy')

output :

응용(+ tick_params)

지금까지 배웠던걸 이용해 연습을 하는 시간을 가져보겠습니다. 

 

우리의 목표는 아래와 같은 그림입니다. 

1. left 축, bottom축을 set_visible('false')를 통해 지워줍니다.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (5,5))


for spine_loc, spine in ax.spines.items():
    if spine_loc in ['left', 'bottom']:
        spine.set_visible(False)

output :

축을 없애고 나니 눈금라벨과 눈금이 남아 있는 상태가 되었습니다 

이때 우리가 해야될건 left에 있는 눈금과, 눈금선을 오른쪽으로. bottom에 있는 눈금과, 눈금선을 top 으로 올려줘야 하는데요..

이때 사용하는것이 tick_params입니다.(tick_params에 대한 내용은 해당 포스트를 참조바랍니다.)

 

2. tick_params에 인자로 필요한 부분은 True로 불필요한것은 False를 지정해줌으로써 조정합니다.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize= (5,5))


for spine_loc, spine in ax.spines.items():
    if spine_loc in ['left', 'bottom']:
        spine.set_visible(False)
        
ax.tick_params(left=False, labelleft=False,
              right = True, labelright=True,
              bottom=False, labelbottom=False,
              top=True, labeltop=True)

output :

 

 

두번째 연습으로는 우리가 opecnv나 Pil 를 이용해 이미지를 가져오면 아래와 같이 숫자도 같이 뜨게되는데요. 어떻게 없애줄까요?(여기선 임의로 더미이미지를 생성하였습니다.)

import matplotlib.pyplot as plt
import numpy as np
np.random.seed(0)

### 이미지 만드는 코드 ###
img_1 = np.random.normal(0, 1, (100,100))
fig, ax = plt.subplots(figsize= (7,7))
ax.imshow(img_1)
### 이미지 만드는 코드 ###

for spine_loc, spine in ax.spines.items(): #가장자리 선 없애기
    spine.set_visible(False)

ax.tick_params(left=False, labelleft=False,            
              bottom=False, labelbottom=False) # tick , ticklabel 없애기
           

output :

간단합니다! set_visible(False)를 통해 축을 전체 다없애주면 됩니다. 이번엔 특정 축만 이 아니라 전체 축이니까 굳이 if 조건절을 사용하지 않아도 되겠죠? 

그 다음에는 tick_params 를 통해 tick과 ticklabel을 없애주면 깔끔하게 그림만 나오는 형태가 됩니다. 

 

축 위치 조정 

앞서 set_position('center') 을 통해 간단하게 위치를 조정하는것을 알아보았는데요. 

위치 조정에 대해 조금더 심도 있게 알아보도록 하겠습니다. 

 

우선 sin그래프를 통해 공부를 시작해보죠.

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(-10, 10, 500)
sin = np.sin(t)

fig, ax = plt.subplots(figsize=(20,7))
ax.plot(t, sin)

ax.tick_params(labelsize=20)

output :

 

어떤가요..? 좀 아쉽지 않나요.. 우리가 아는 일반적인 그래프는 x축이 y축 과 함께 어우러진 함수형태가 아니죠..

 

우선 저는 top, right 축을 없애고, left축과 bottom 축을 가운데로 해보고 싶네요.

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(-10, 10, 500)
sin = np.sin(t)

fig, ax = plt.subplots(figsize=(20,7))
ax.plot(t, sin)

ax.tick_params(labelsize=20)

for spine_loc, spine in ax.spines.items():
    if spine_loc in ['right', 'top']:
        spine.set_visible(False)
        
    if spine_loc in ['bottom' , 'left']:
        spine.set_position('center')         

output :

이제 좀 그럴싸한가요?

참고로 set_position의 경우 

set_position(('axes', 0.5))와 같이 두개의 파라미터를 갖는 튜플 형태도 있습니다.

해당 코드는 set_position('center')와 같습니다. 

 

참고로 숫자는 0~1을 기준으로 하는데요. 0.5는 당연히 가운데겠죠? 1로 갈수록 축은 위 또는 오른쪽으로 갈것입니다. 

궁금하신분은 직접 테스트해보세요!

 

이번엔 두번째 예시입니다. 

평균이 0, 표준편차가 1인 정규분포형태의 noise 데이터를 만들어 보았습니다. 


import numpy as np
import matplotlib.pyplot as plt


np.random.seed(0)

t_min, t_max = 0, 10
n_data = 100

t = np.linspace(t_min, t_max, n_data)
noise_data = np.random.normal(0, 1, size = (n_data,))
fig, ax= plt.subplots(figsize=(7,7))
ax.plot(t, noise_data)

output :

해당그래프도 뭔가 아쉽죠?

평균을 중심으로 얼마나 노이즈가 있는지 알아보기 위해선 앞선 sin 그래프처럼 각축들을 가운데로 모아 줘야겠네요.

 

import numpy as np
import matplotlib.pyplot as plt


np.random.seed(0)

t_min, t_max = 0, 10
n_data = 100

t = np.linspace(t_min, t_max, n_data)
noise_data = np.random.normal(0, 1, size = (n_data,))

fig, ax= plt.subplots(figsize=(7,7))
ax.plot(t, noise_data)

ax.tick_params(labelsize=15)
fig.tight_layout()

for spine_loc, spine in ax.spines.items():
    if spine_loc in ['right', 'top']:
        spine.set_visible(False)
    if spine_loc in ['bottom']:
        spine.set_position('center')

output :

엇. 근데 뭔가 이상합니다. 0이 서로 맞물리지 않고 벌어져있네요.

이럴땐 어떻게 해야할까요?

 

set_position('center')을 set_position(('data',0)) 형태로 바꿔주면 됩니다.!

앞서말씀드린 대로 set_position('center') = set_position(('axis',0)) 은 같다고 했었죠.

'axis'가 아닌 'data'가 들어간것이 포인트입니다.!

 

다시정리하자면,

axis는 전체 축을 0~1로 기준으로 해서 비율적으로 위치를 찾아내는것이라면.

data는 원하는 눈금라벨에 축을 맞출수있는것을 의미하죠..

 

정말 그런지 set_position(('data',1))를 지정해볼까요?

import numpy as np
import matplotlib.pyplot as plt


np.random.seed(0)

t_min, t_max = 0, 10
n_data = 100

t = np.linspace(t_min, t_max, n_data)
noise_data = np.random.normal(0, 1, size = (n_data,))

fig, ax= plt.subplots(figsize=(7,7))
ax.plot(t, noise_data)

ax.tick_params(labelsize=15)
fig.tight_layout()

for spine_loc, spine in ax.spines.items():
    if spine_loc in ['right', 'top']:
        spine.set_visible(False)
    if spine_loc in ['bottom']:
        spine.set_position(('data',1))

output :

보시는것처럼 축이 1에 맞춰진것을 확인할 수 있습니다. 

 

 

 

 

  • 트위터 공유하기
  • 페이스북 공유하기
  • 카카오톡 공유하기
이 컨텐츠가 마음에 드셨다면 커피 한잔(후원) ☕

댓글