(2024.12.28) 수정됨.
이것저것 해보다가 놀라운 사실을 알게 되었는데, PolyCollection의 작업이 굉장히 느리다는 것이다.
또한 LineCollection으로 PolyCollection의 작업을 대체할 수 있다는 것을 함께 알게 되었다.
먼저 다음 코드를 보자.
단순하게 1만 개의 폴리곤, 마름모를 그리는 코드다.
from time import time
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection, PolyCollection
xy = [
[
(2, 3),
(1, 2),
(2, 1),
(3, 2),
(2, 3),
],
]
for _ in range(9999): xy.append([(x+2, y+2) for x, y in xy[-1]])
def lim(_):
if ax.get_xlim()[1] < 11:
ax.set_xlim(0, xy[-1][0][0]+200)
ax.set_ylim(0, xy[-1][2][1]+200)
else:
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
fig.canvas.draw()
return
fig, ax = plt.subplots(1, 1)
fig.canvas.mpl_connect('button_press_event', lim)
ax.set_xlim(0, xy[-1][0][0]+200)
ax.set_ylim(0, xy[-1][2][1]+200)
t = time()
plt.title('draw PolyCollection')
pc = PolyCollection(xy, edgecolors=['y', 'b'], facecolors=['r', 'g'], alpha=[0.2, 1], linewidth=3)
ax.add_collection(pc)
# plt.title('draw LineCollection')
# lc = LineCollection(xy, edgecolors=['y', 'b'], facecolors=['r', 'g'], alpha=[0.2, 1], linewidth=3)
# ax.add_collection(lc)
t2 = time() - t
print(f'{t2=}')
plt.show()
단순하게 1만개의 마름모를 그리는 코드다.
코드를 실행하면 PolyCollection을 이용해 마름모를 그린다. 다음은 위 코드를 5회 실행한 결과다.
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.257997989654541
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.16300010681152344
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.17500066757202148
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.15801501274108887
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.1530156135559082
느린 경우 0.26초가 걸리긴 하지만, 대체로 0.15초 정도에 작업이 끝나는 것을 알 수 있다.
개체 수가 많다보니 대각선으로 보이기도 한다. 의심된다면 그래프를 클릭해보자.
그래프를 클릭하면 확대되도록 이벤트를 연결해두었다.
LineCollection을 쓴다면?
이번에는 라인콜렉션을 그려 폴리콜렉션과 얼마나 차이가 나는지 확인해보자.
위 코드에서 주석을 제거해도 되고, 다음 코드를 복사해서 실행해봐도 된다.
from time import time
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection, PolyCollection
xy = [
[
(2, 3),
(1, 2),
(2, 1),
(3, 2),
(2, 3),
],
]
for _ in range(9999): xy.append([(x+2, y+2) for x, y in xy[-1]])
def lim(_):
if ax.get_xlim()[1] < 11:
ax.set_xlim(0, xy[-1][0][0]+200)
ax.set_ylim(0, xy[-1][2][1]+200)
else:
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
fig.canvas.draw()
return
fig, ax = plt.subplots(1, 1)
fig.canvas.mpl_connect('button_press_event', lim)
ax.set_xlim(0, xy[-1][0][0]+200)
ax.set_ylim(0, xy[-1][2][1]+200)
t = time()
# plt.title('draw PolyCollection')
# pc = PolyCollection(xy, edgecolors=['y', 'b'], facecolors=['r', 'g'], alpha=[0.2, 1], linewidth=3)
# ax.add_collection(pc)
plt.title('draw LineCollection')
lc = LineCollection(xy, edgecolors=['y', 'b'], facecolors=['r', 'g'], alpha=[0.2, 1], linewidth=3)
ax.add_collection(lc)
t2 = time() - t
print(f'{t2=}')
plt.show()
마찬가지로 5번 실행한 결과다. 0.67~0.91초 정도가 소요되었다.
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.08199858665466309
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.08300113677978516
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.09099745750427246
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.0879976749420166
> & ".../Python311/python.exe" ".../line vs poly.py"
t2=0.06699752807617188
결과물을 확인해보면 폴리콜렉션과 마찬가지로 마름모를 그린 것을 알 수 있다.
마름모 형태의 폴리곤을 그리는데 LineCollection을 사용하는 것이 PollyCollection을 사용하는 것보다 거의 2배, 심한 경우 5배의 속도 차이를 보여주고 있다.
거기에 더해 PollyCollection에서 LineCollection으로 변환하는 작업은 어렵지 않다.
단순히 사용하는 class를 PollyCollection에서 LineCollection으로 변경하면 끝일 뿐이다.
LineCollection으로 그린 마름모의 경우 12시 꼭지점 하나가 날카롭지 않고 쪼개져있는 것이 신경쓰이기는 한데, 끝에 좌표를 하나 추가해 부족한 부분을 메우면 된다.