1. plt marker custom
plt(matplotlib.pyplot)를 이용하여 그래프를 그릴때 표시되는 점(marker)을 내가 원하는 아이콘으로 커스텀해서 사용하고자 한다.
1-1. 아이콘 선택
"window + . >" 키를 누르면 이모지가 나오는데, 스마일 아이콘을 가져왔다.
메모장에 아이콘을 표시하고 캡쳐 도구로 이미지(.PNG)를 만들었다.
1-2. .svg 포멧
svg란? 확대를 해도 깨지지 않는 벡터기반 그래픽이라고 한다.(자세한건 wiki에게 물어보는거로...)
.svg 포멧이면 matplotlib에서 마커로 사용 가능하다.
adobe 홈페이지에서 무료로 PNG파일을 svg 파일로 변환 가능하다.(참고자료 1)
1-3. 아이콘 불러오기
# 라이브러리를 가져온다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib as mpl
from matplotlib.path import Path
from svgpathtools import svg2paths
from svgpath2mpl import parse_path
import matplotlib.pyplot as plt
# svg파일을 불러온다.
icon_path, attributes = svg2paths('02_icon.svg')
icon_marker_0 = parse_path(attributes[0 ]['d'])
icon_marker_1 = parse_path(attributes[1 ]['d'])
icon_marker_2 = parse_path(attributes[2 ]['d'])
icon_marker_3 = parse_path(attributes[3 ]['d'])
icon_marker_4 = parse_path(attributes[4 ]['d'])
icon_marker_5 = parse_path(attributes[5 ]['d'])
icon_marker_6 = parse_path(attributes[6 ]['d'])
icon_marker_7 = parse_path(attributes[7 ]['d'])
icon_marker_8 = parse_path(attributes[8 ]['d'])
icon_marker_9 = parse_path(attributes[9 ]['d'])
icon_marker_10 = parse_path(attributes[10]['d'])
icon_marker_11 = parse_path(attributes[11]['d'])
icon_marker_12 = parse_path(attributes[12]['d'])
icon_marker_13 = parse_path(attributes[13]['d'])
plt.plot(0,0,marker=icon_marker_0 ,markersize=300)
plt.plot(0,0,marker=icon_marker_1 ,markersize=300)
plt.plot(0,0,marker=icon_marker_2 ,markersize=300)
plt.plot(0,0,marker=icon_marker_3 ,markersize=300)
plt.plot(0,0,marker=icon_marker_4 ,markersize=300)
plt.plot(0,0,marker=icon_marker_5 ,markersize=300)
plt.plot(0,0,marker=icon_marker_6 ,markersize=300)
plt.plot(0,0,marker=icon_marker_7 ,markersize=300)
plt.plot(0,0,marker=icon_marker_8 ,markersize=300)
plt.plot(0,0,marker=icon_marker_9 ,markersize=300)
plt.plot(0,0,marker=icon_marker_10 ,markersize=300)
plt.plot(0,0,marker=icon_marker_11 ,markersize=300)
plt.plot(0,0,marker=icon_marker_12 ,markersize=300)
plt.plot(0,0,marker=icon_marker_13 ,markersize=300)
.svg로 변환하는 과정에서 다 깨졌다..
2번인 스마일 모양만 사용하기로 한다.
1-4. 아이콘 위치 조정
icon_marker = icon_marker_2.copy()
plt.plot(0,0,marker=icon_marker ,markersize=300)
# 중심위치와 각도가 안맞는다.
# 먼저 중심위치를 맞추기 위해 전체 크기를 확인한다
x_min = icon_marker.vertices[:, 0].min()
x_max = icon_marker.vertices[:, 0].max()
y_min = icon_marker.vertices[:, 1].min()
y_max = icon_marker.vertices[:, 1].max()
print('x_max : ', x_max, 'x_min : ', x_min, 'y_max : ', y_max, 'y_min : ', y_min)
# 전체 크기를 고려하여 대략적으로 중심을 맞춘다.
icon_marker.vertices -= np.array([50,50])
# 각도는 180도 돌린다.
icon_marker = icon_marker.transformed(mpl.transforms.Affine2D().rotate_deg(180))
plt.plot(0,0,marker=icon_marker,markersize=300)
x_max : 96.549393 x_min : 11.462228 y_max : 91.352959 y_min : 8.093739
parse_path로 가져온 데이터는 vertices와 codes로 구분되어있다.
vertices는 이미지를 구성하는 점들(x, y좌표값)이 들어있다.
codes는 숫자가 들어있는 리스트이다.(아마도 점들을 잇는 곡선에 대한 정보가 아닐까 싶다.)
# 중심을 다시 맞춘다.
x_min = icon_marker.vertices[:, 0].min()
x_max = icon_marker.vertices[:, 0].max()
y_min = icon_marker.vertices[:, 1].min()
y_max = icon_marker.vertices[:, 1].max()
icon_marker.vertices -= np.array([(x_max + x_min) / 2,(y_max + y_min) / 2])
살짝 안맞는거같은데,,, 일단 쓰기로 한다.
2. 회전 애니메이션
# 애니메이션
fig , ax= plt.subplots(constrained_layout=True)
def animate(i):
i = np.arange(0, 470, 12)[i]
plt.cla()
icon_marker_rotate = icon_marker.transformed(mpl.transforms.Affine2D().rotate_deg(i))
ref_line, = ax.plot(0, 0, marker = icon_marker_rotate, markersize = 200, c = 'k')
return ref_line
anim = FuncAnimation(fig, animate, frames=30, blit=False)
anim.save("icon_marker_rotate.gif", fps=30)
(비웃는듯한 느낌이 드는건 과연 기분탓인가...)
중심이 안맞거나, 반지름이 일정한 원이 아니라면 이렇게 회전하면서 찌그러진다.
여기서 사용한 방법은, matplotlib 그래프를 frame수 만큼 그래프를 그리는 방법이다.
plt.cla()로 지우면서 그려야 한다. 그렇지 않으면 시작 이미지부터 계속 누적되어 표시된다.
회전을 위해 transform.Affine2D를 사용하였다.
반지름이 일정한 원형이 아니라면, 위와 같이 회전하면서 스케일이 미묘하게 변하게 된다.
그래서 회전을 시키려면 아이콘은 반지름이 일정한 원형이어야 하며, 원형이 아닐 경우 배경에 원을 삽입한다.
3. 회전 애니메이션(한번 더)
좀더 원형에 가까운 이미지로 다시 테스트해본다.
svgsilh라는 곳에서 svg파일을 무료로 다운받을 수 있다.(참고자료 2)
sample_path, attributes = svg2paths('02_sample.svg')
sample_marker = parse_path(attributes[0]['d'])
x_min = sample_marker.vertices[:, 0].min()
x_max = sample_marker.vertices[:, 0].max()
y_min = sample_marker.vertices[:, 1].min()
y_max = sample_marker.vertices[:, 1].max()
print('원점 : ', x_max + x_min, y_max + y_min, 'x_max : ', x_max, 'x_min : ', x_min, 'y_max : ', y_max, 'y_min : ', y_min)
sample_marker.vertices -= np.array([(x_max + x_min) / 2,(y_max + y_min) / 2])
x_min = sample_marker.vertices[:, 0].min()
x_max = sample_marker.vertices[:, 0].max()
y_min = sample_marker.vertices[:, 1].min()
y_max = sample_marker.vertices[:, 1].max()
print('원점 : ', x_max + x_min, y_max + y_min, 'x_max : ', x_max, 'x_min : ', x_min, 'y_max : ', y_max, 'y_min : ', y_min)
sample_marker_rotate = sample_marker.transformed(mpl.transforms.Affine2D().rotate_deg(20))
plt.plot(0, 0, marker = sample_marker_rotate, markersize = 200, c = 'r')
sample_marker_rotate = sample_marker.transformed(mpl.transforms.Affine2D().rotate_deg(15))
plt.plot(0, 0, marker = sample_marker_rotate, markersize = 200, c = 'y')
sample_marker_rotate = sample_marker.transformed(mpl.transforms.Affine2D().rotate_deg(10))
plt.plot(0, 0, marker = sample_marker_rotate, markersize = 200, c = 'g')
sample_marker_rotate = sample_marker.transformed(mpl.transforms.Affine2D().rotate_deg(5))
plt.plot(0, 0, marker = sample_marker_rotate, markersize = 200, c = 'b')
원점 : 12826.0 12662.0 x_max : 12833.0 x_min : -7.0 y_max : 12688.0 y_min : -26.0
원점 : 0.0 0.0 x_max : 6420.0 x_min : -6420.0 y_max : 6357.0 y_min : -6357.0
중심이 잘 맞는 것 같다.
회전 애니메이션을 만들어본다.
# 애니메이션
fig , ax= plt.subplots(constrained_layout=True)
def animate(i):
i = np.arange(0, 470, 12)[i]
plt.cla()
sample_marker_rotate = sample_marker.transformed(mpl.transforms.Affine2D().rotate_deg(i))
ref_line, = ax.plot(0, 0, marker = sample_marker_rotate, markersize = 200, c = 'k')
return ref_line
anim = FuncAnimation(fig, animate, frames=30, blit=False)
anim.save("smple_marker_rotate.gif", fps=30)
분명 중심을 맞췄는데..?!
4. 주의사항
4-1. 모니터 스펙과 아이콘 크기
결국 모니터에서 어떻게 보이느냐가 중요하기 때문에,
모니터 스펙(가로 * 세로 pixel, pixel per mm)을 확인한다.
모니터 상에서 아이콘을 몇 mm 표시할지 계산하려면 필요하다.
4-2. matplotlib.animation(참고자료 4)
frames : 총 프레임
intervals : frame 사이 시간 간격[ms]
이에따른 rotate_deg 값에 의해 회전속도를 조절할 수 있다.
그냥 끝내긴 아쉬워서....
x = np.arange(0, 20, 1)
y = np.cos(x)
plt.plot(x, y, marker=icon_marker_modi, markersize=30)
※ 참고자료
1. https://www.adobe.com/kr/express/feature/image/convert/png-to-svg
무료로 JPG 이미지를 SVG로 변환 | Adobe Express
Adobe Express의 무료 PNG-SVG 변환기로 PNG 이미지를 SVG로 변환하세요. 쉽고 빠르게 PNG 이미지를 업로드하고 SVG 파일로 변환할 수 있습니다.
www.adobe.com
2. svg 파일 모아놓은 사이트
https://svgsilh.com/ko/image/1091576.html
3. plt의 marker를 custom으로 만드는 방법
https://petercbsmith.github.io/marker-tutorial.html
Custom Markers
This tutorial is also available as a Jupyter Notebook here Add a little personal touch or more options for clarity to your scatter plots with your own custom markers! In this short tutorial I'll explain step by step how you can create and use virtually any
petercbsmith.github.io
4. matplolib animation 사용 방법
https://matplotlib.org/stable/api/_as_gen/matplotlib.animation.FuncAnimation.html
matplotlib.animation.FuncAnimation — Matplotlib 3.6.2 documentation
TimedAnimation subclass that makes an animation by repeatedly calling a function func. Note You must store the created Animation in a variable that lives as long as the animation should run. Otherwise, the Animation object will be garbage-collected and the
matplotlib.org
5. 축을 기준으로 회전시킬 경우
https://jehyunlee.github.io/2022/08/05/Python-DS-110-anim/
Matplotlib Animation
정지된 그림으로는 볼 수 없는 것들이 있습니다. 시간에 따른 변화나 입체 도형의 뒷면이 그것입니다. 애니메이션을 활용해 이를 보완합니다. 1. Matplotlib animation matplotlib.animation Matplotlib에서 사
jehyunlee.github.io
'데이터 분석 > 시각화' 카테고리의 다른 글
pandas로 논문 형식의 table 작성하기 (0) | 2023.01.02 |
---|
댓글