본문 바로가기
Python/Matplotlib

Matplotlib의 성능 개선 방법 - matplotlib(5)

by 콩돌 2019. 4. 6.
반응형

파이썬 버전 3.7 기준

matplotlib 버전 3.0.3 기준



  Matplotlib의 성능 개선 방법


본 포스팅에서는 Matplotlib의 성능 개선 방법에 대해서 다룬다.

 

  Matplotlib의 성능 개선 방법

 

대화형 모드에서 데이터를 탐색하던 혹은 프로그램 적으로 많은 플롯을 저장할 때, 랜더링 작업에 많은 시간이 소요될 수 있다(특히 데이터가 수가 많거나 플롯의 수가 많은 경우 더 그렇다.). 

matplotlib는 플롯의 외관의 약간의 변경(허용 가능한 오차범위 내에서)을 통해 랜더링 시간을 상당히 줄일 수 있는 2개의 방법을 제공한다. 

랜더링 시간을 줄이기 위해 사용 가능한 방법은 만들고자하는 플롯 타입에 의존한다.

 

 

  선분(Line segment)의 단순화

선분(전형적인 라인플롯, 다각형의 아웃라인 기타 등등)을 가지는 플롯에 대해서는 랜더링 성능은 matplotlibrc 파일 내부의 path.simplifypath.simplify_threshold 파라미터에 의해 제어될 수 있다. 

path.simplify 파라미터는 선분 단순화의 수행여부를 결정하는 논리형(boolean) 플래그이다. 

path.simplify_threshold 파라미터는 선분을 단순화하는 규모를 제어한다. 이 수치를 키울 수록 더 빠르게 랜더링을 할 수 있게 한다.

 

아래의 스크립트는 먼저 단순화없이 그리고 단순화 있이 데이터를 플롯하는 예제이다. 

둘 다 시도하면 플롯을 그리는 시간이 얼마나 단축되는지 확인할 수 있다.


스크립트 예제)

# 필요한 패키지 및 모듈을 import

import numpy as np

import matplotlib.pyplot as plt

import matplotlib as mpl

import time


# 플롯 setup

y = np.random.rand(100000)

y[50000:] *= 2

y[np.logspace(1, np.log10(50000), 400).astype(int)] = -1

mpl.rcParams['path.simplify'] = True


# 첫번째 플롯 작성(단순화 실행 X)

start_time = time.time()            # 플롯 작성 시작하는 시간 입력

mpl.rcParams['path.simplify_threshold'] = 0.0     # 플롯 단순화 사실상 실행안함

plt.plot(y)                              # 플롯 데이터 입력

plt.draw()                              # 플롯 그리기, 만약 화면에 그래프가 출력되는 것을 보고싶다면 show로 변환할 것

end_time = time.time()            # 플롯 작성 끝내는 시간 입력

print(end_time-start_time)        # 플롯 작성에 소요되는 시간 출력


# 두번째 플롯 작성(단순화 실행 O)

start_time = time.time()           

mpl.rcParams['path.simplify_threshold'] = 1.0

plt.plot(y)

plt.draw()

end_time = time.time()

print(end_time-start_time)        # 플롯 작성에 소요되는 시간 출력

※ 화면에 그래프가 뜨는 것을 보고싶다면, plt.draw()를 plt.show()로 바꾸면된다. 대신 이 경우 그래프가 화면에 떠있는 시간이 플롯작성 시간에 포함되므로 print(end_time-start_time)로 그래프가 출력되는 시간은 의미가 없어진다.


matplotlib에는 현재 path.simplify_threshold의 값을 보수적으로 1/9을 기본값으로 설정되어있다. 

만약 사용자가 이 기본 세팅을 바꾸길 원한다면 matplotlibrc파일을 편집하면 된다. 

사용자는 대화형 모드 플롯팅에 대해 빠른 출력을 할 수 있는 스타일을 만들 수 있으며, 제대로 사용(보고서 첨부 등)하기 위한 질좋은 플로팅을 출력하기 위한 스타일을 따로 만들 수도 있다. 

만약 그것들이 필요한 경우 활성화 시키는 것도 가능하다. 

자세한 사항을 알고 싶다면 Customizing Matplotlib with style sheets and rcParams을 참고할 수 있다.


단순화 작업은 다음 선분의 벡터에 대한 수직 거리(디스플레이 좌표 공간에서 측정되어진)가 path.simplify_threshold 매개 변수보다 커질 때까지 선분을 단일 벡터로 반복적으로 병합하여 작업한다.


참고사항

  어떻게 선분을 단순화 할 건지에 관련된 변경은 2.1 버전에서 생겼다. 위의 설명된 변수의 변경으로 랜더링 시간은 2.1이전 버전에서는 어느정도 개선되긴 하지만, 2.1이상 버전에서는 상당히 개선된다.



  마커(Marker)의 단순화

선분의 단순화보다는 영향이 적지만, 마커 역시 단순화될 수 있다. 

마커의 단순화는 Line2D 객체에서만 가능하다(markevery 속성을 통해서 가능하다.). 

matplotlib.pyplot.plot()이나 matplotlib.axes.Axes.plot()과 같이 Line2D 구축 파라미터를 사용하는 곳이면 어디든, markevery 파라미터가 사용되어질 수 있다.


plt.plot(a, b, markevery=10)

 

markevery 독립변수는 일반적인 서브샘플링에 대해 혹은 균등하게 나누어진 샘플링을 하도록 한다. 

자세한 사항은 Markevery Demo에서 확인할 수 있다.



  더 작은 요소(Chunk)로 선 나누기 

만약 사용자가 Agg 백엔드를 사용할 경우에는, 사용자는 rc 파라미터에서 agg.path.chunksize를 사용할 수 있다. 

이 변수는 요소 사이즈를 설정할 수 있게하며, 그 점보다 더 많은 점을 가진 선은 여러 선으로 나뉘어 지게 되며, 각각의 선은 agg.path.chunksize의 것보다 더 적은 점의 수를 가진다.  

몇몇 종류의 데이터에 대해서는, 적절한 크기로 선을 덩어리로 나누는 것은 랜더링 시간을 크게 줄일 수 있다.

 

다음 예제 스크립트는 먼저 chunk 사이즈 제한 없이 화면에 데이터를 띄우며 그뒤에는 같은 데이터를 chunk의 사이즈를 10000으로 설정하여 하면에 출력하는 예제이다. 

figure가 클 수록 아래의 차이는 크게 보일 것이다. 

아래의 예제를 GUI를 최대로하여 시도해보고 비교해 보자.


# 필요한 패키지 및 모듈을 import

import numpy as np

import matplotlib.pyplot as plt

import matplotlib as mpl

import time


mpl.rcParams['path.simplify_threshold'] = 1.0


# 플롯 setup

y = np.random.rand(100000)

y[50000:] *= 2

y[np.logspace(1,np.log10(50000), 400).astype(int)] = -1

mpl.rcParams['path.simplify'] = True


# 첫번째 플롯 작성(단순화 실행 X)

start_time = time.time()            # 플롯 작성 시작하는 시간 입력

mpl.rcParams['agg.path.chunksize'] = 0     # 플롯 단순화 사실상 실행안함

plt.plot(y)                              # 플롯 데이터 입력

plt.draw()                              # 플롯 그리기, 만약 화면에 그래프가 출력되는 것을 보고싶다면 show로 변환할 것

end_time = time.time()            # 플롯 작성 끝내는 시간 입력

print(end_time-start_time)        # 플롯 작성에 소요되는 시간 출력


# 두번째 플롯 작성(단순화 실행 O)

start_time = time.time()

mpl.rcParams['agg.path.chunksize'] = 10000

plt.plot(y)

plt.draw()

end_time = time.time()

print(end_time-start_time)

 ※ 화면에 그래프가 뜨는 것을 보고싶다면, plt.draw()를 plt.show()로 바꾸면된다. 대신 이 경우 그래프가 화면에 떠있는 시간이 플롯작성 시간에 포함되므로 print(end_time-start_time)로 그래프가 출력되는 시간은 의미가 없어진다.


  레전드(Legend)

축에 대한 레전드는 가장 적게 데이터 포인트를 가리는 위치가 기본값으로 지정된다(loc='best'). 

만약 데이터의 수가 매우 많을 경우에는, 이것은 매우 상당한 연산 시간을 소요한다. 

이런 경우 사용자는 명확한 위치를 지정하는 것이 좋다.



 Fast Style의 사용

페스트 스타일(Fast Style)은 처리하고자 하는 데이터가 많은 경우 플로팅 속도를 개선하기 위해 자동적으로 단순화를 세팅하고, chunking파라미터를 합리적이게 셋팅하는데 사용된다. 

이것은 아래 예제처럼 단순하게 사용될 수 있다.


import matplotlib.style as mplstyle

mplstyle.use('fast')

 

이 설정은 매우 가벼우며, 그래서 매우 나이스하게 다른 스타일을 사용한다. 

다만 페스트 스타일이 적용되어질 때 다른 스타일의 세팅이 덮어씌워지지 않도록 주의하자.


mplstyle.use(['dark_background', 'ggplot', 'fast'])

 


 

 

 

 

 참고자료

  https://matplotlib.org/tutorials/introductory/usage.html#sphx-glr-tutorials-introductory-usage-py

 

 

 

 

 

반응형

댓글