1. ホーム
  2. python

[解決済み] なぜ Matplotlib でのプロットはこんなに遅いのですか?

2022-08-28 10:13:27

疑問点

私は現在、様々なPythonのプロットライブラリを評価しています。今、私はmatplotlibを試していますが、その性能にはかなり失望しています。次の例は、以下のものを修正したものです。 SciPyの例 から修正したもので、1秒間にたった8フレームしか表示されません。

これを高速化する方法はありますか、それとも別のプロットライブラリを選ぶべきですか?

from pylab import *
import time

ion()
fig = figure()
ax1 = fig.add_subplot(611)
ax2 = fig.add_subplot(612)
ax3 = fig.add_subplot(613)
ax4 = fig.add_subplot(614)
ax5 = fig.add_subplot(615)
ax6 = fig.add_subplot(616)

x = arange(0,2*pi,0.01)
y = sin(x)
line1, = ax1.plot(x, y, 'r-')
line2, = ax2.plot(x, y, 'g-')
line3, = ax3.plot(x, y, 'y-')
line4, = ax4.plot(x, y, 'm-')
line5, = ax5.plot(x, y, 'k-')
line6, = ax6.plot(x, y, 'p-')

# turn off interactive plotting - speeds things up by 1 Frame / second
plt.ioff()


tstart = time.time()               # for profiling
for i in arange(1, 200):
    line1.set_ydata(sin(x+i/10.0))  # update the data
    line2.set_ydata(sin(2*x+i/10.0))
    line3.set_ydata(sin(3*x+i/10.0))
    line4.set_ydata(sin(4*x+i/10.0))
    line5.set_ydata(sin(5*x+i/10.0))
    line6.set_ydata(sin(6*x+i/10.0))
    draw()                         # redraw the canvas

print 'FPS:' , 200/(time.time()-tstart)

どのように解決するのですか?

まず最初に、(これは全くパフォーマンスを変えませんが)以下のように、コードをきれいにすることを検討してください。

import matplotlib.pyplot as plt
import numpy as np
import time

x = np.arange(0, 2*np.pi, 0.01)
y = np.sin(x)

fig, axes = plt.subplots(nrows=6)
styles = ['r-', 'g-', 'y-', 'm-', 'k-', 'c-']
lines = [ax.plot(x, y, style)[0] for ax, style in zip(axes, styles)]

fig.show()

tstart = time.time()
for i in xrange(1, 20):
    for j, line in enumerate(lines, start=1):
        line.set_ydata(np.sin(j*x + i/10.0))
    fig.canvas.draw()

print 'FPS:' , 20/(time.time()-tstart)

上記の例では、10fps程度になります。

ちょっとしたメモですが、あなたの正確なユースケースによっては、matplotlibは良い選択ではないかもしれません。 リアルタイム表示ではなく、出版物レベルの数値に向いています。

しかし、この例を高速化するためにできることがたくさんあります。

これがこれほど遅い理由は主に2つあります。

1) 呼び出し fig.canvas.draw() 再描画 すべて . それがボトルネックになっています。 あなたの場合、軸の境界線、目盛りラベルなどのようなものを再描画する必要はありません。

2) あなたのケースでは、多くの目盛りラベルを持つ多くのサブプロットがあります。 これらは描画に長い時間がかかります。

これらはいずれもブリッティングを使用することで解決することができます。

ブリッティングを効率的に行うには、バックエンドに特化したコードを使用する必要があります。 実際には、滑らかなアニメーションを本当に心配するのであれば、通常、何らかのGUIツールキットにmatplotlibのプロットを埋め込むでしょうから、これはあまり問題ではありません。

しかし、あなたが何をしているのかもう少し知らなければ、私はそこであなたを助けることができません。

それにもかかわらず、それを行う GUI ニュートラルな方法があり、それはまだ合理的に速いです。

import matplotlib.pyplot as plt
import numpy as np
import time

x = np.arange(0, 2*np.pi, 0.1)
y = np.sin(x)

fig, axes = plt.subplots(nrows=6)

fig.show()

# We need to draw the canvas before we start animating...
fig.canvas.draw()

styles = ['r-', 'g-', 'y-', 'm-', 'k-', 'c-']
def plot(ax, style):
    return ax.plot(x, y, style, animated=True)[0]
lines = [plot(ax, style) for ax, style in zip(axes, styles)]

# Let's capture the background of the figure
backgrounds = [fig.canvas.copy_from_bbox(ax.bbox) for ax in axes]

tstart = time.time()
for i in xrange(1, 2000):
    items = enumerate(zip(lines, axes, backgrounds), start=1)
    for j, (line, ax, background) in items:
        fig.canvas.restore_region(background)
        line.set_ydata(np.sin(j*x + i/10.0))
        ax.draw_artist(line)
        fig.canvas.blit(ax.bbox)

print 'FPS:' , 2000/(time.time()-tstart)

これで〜200fpsになりました。

これをもう少し便利にするために animations モジュールがあります。

例として

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

x = np.arange(0, 2*np.pi, 0.1)
y = np.sin(x)

fig, axes = plt.subplots(nrows=6)

styles = ['r-', 'g-', 'y-', 'm-', 'k-', 'c-']
def plot(ax, style):
    return ax.plot(x, y, style, animated=True)[0]
lines = [plot(ax, style) for ax, style in zip(axes, styles)]

def animate(i):
    for j, line in enumerate(lines, start=1):
        line.set_ydata(np.sin(j*x + i/10.0))
    return lines

# We'd normally specify a reasonable "interval" here...
ani = animation.FuncAnimation(fig, animate, xrange(1, 200), 
                              interval=0, blit=True)
plt.show()