なぜMatplotlibでのプロットがとても遅いのですか?


100

現在、さまざまな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)

以下が該当する可能性があります:stackoverflow.com/questions/5003094/…–
NPE

2
@aix-Glumpyは、画像データの高速表示を扱っていたため、この例でのみ役立ちました。この場合は役に立ちません。
Joe Kington、2012年

1
バックエンドを変更してみてください。私の回答をご覧ください:stackoverflow.com/a/30655528/2066079。またはバックエンドに関するこのFAQ:matplotlib.org/faq/usage_faq.html#what-is-a-backend
dberm22

回答:


115

まず、(これによってパフォーマンスはまったく変わりません)、次のようにコードをクリーンアップすることを検討してください:

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)あなたの場合、目盛りラベルがたくさんあるサブプロットがたくさんあります。これらの描画には時間がかかります。

これらはどちらもブリッティングを使用して修正できます。

ブリットを効率的に行うには、バックエンド固有のコードを使用する必要があります。実際には、スムーズなアニメーションが本当に心配な場合は、通常、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、matplotlibの最近のバージョンにはモジュールがあります。

例として:

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()

あなたのコードは確かに非常に高速ですが、最終的には軸あたり2000行になります!どういうわけか「line.set_ydata」はそれを更新するのではなく新しい行を作成します-または背景は単にクリアされていませんか?さらに、あなたのバージョンはなぜそんなに速いのですか?「draw()」をドロップして「ax.draw_artist」に置き換えただけですか?
memyself 2012年

どの例で?(私はそれらをテストしましたが、答えに間違ったバージョンをコピーして貼り付けた可能性があります。)また、使用しているmatplotlibのバージョンは何ですか?
Joe Kington、2012年

4
ここに結果の画像へのリンクがありますi.imgur.com/aBRFz.pngこれは私のグラフィックスカードが原因のアーティファクトである可能性がありますか?
memyself 2012年

7
背景のキャプチャをfig.show()のに移動するまで、memyselfがi.imgur.com/aBRFz.pngで見ていたのと同じものを見ていました。
Michael Browne 2013年

4
いいですがanimationinterval期間ごとにプロットを更新しているようですが、新しいデータの準備ができたときにそれを更新したい場合はどうすればよいですか?
Alcott、2014年

28

Matplotlibは優れた出版品質のグラフィックスを作成しますが、速度については十分に最適化されていません。速度を考慮して設計されたさまざまなpythonプロットパッケージがあります。


1
リアルタイムストリームデータのpyqtgraph.org/documentationを完全に楽しんでいます。素晴らしい仕事
luke

11

まず、ジョー・キントンの回答は、中立的なアプローチを使用して非常に優れたアドバイスを提供します。彼のアドバイス(特にブリッティングについて)を確実に取り入れて、実践に移す必要があります。このアプローチの詳細については、 Matplotlibクックブックを

ただし、プロットを高速化するには、GUIに依存しない(GUIバイアス?)アプローチが重要です。言い換えれば、バックエンドは速度をプロットするために非常に重要です。

matplotlibから他のものをインポートする前に、次の2行を配置します。

import matplotlib
matplotlib.use('GTKAgg') 

もちろん、代わりに使用するさまざまなオプションがあります GTKAgg、前述のクックブックによると、これが最速でした。その他のオプションについては、バックエンドに関するリンクを参照してください。


これはWindowsでのみ機能しますが、Macで機能させる方法を知っていますか。それがウィンドウ固有である理由は、
pygtk

2
pygtkはウィンドウ固有ではありません。実際、それをWindowsで動作させるのは大変な苦痛です(それが可能な場合でも、私はあきらめました。)
Joseph Redfern

7

Joe Kingtonが提案した最初のソリューション(.copy_from_bbox&.draw_artist&canvas.blit)では、後で背景をキャプチャする必要がありました。、fig.canvas.draw()行の必要がありました。あなたが言及した。これをfig.show()の後に置いても、Michael Browneの提案どおりには機能しません。

したがって、canvas.draw()の後に背景行を配置します。

[...]
fig.show()

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

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

4
別の回答として投稿するのではなく、彼の回答を編集するだけです
エンドリス

1

これは多くの人には当てはまらないかもしれませんが、私は通常Linuxでコンピュータを操作しているので、デフォルトでmatplotlibプロットをPNGおよびSVGとして保存します。これはLinuxでは正常に動作しますが、Windows 7のインストール(Python(x、y)またはAnacondaでのMiKTeX)では耐えられないほど遅いため、このコードを追加することにしました。

import platform     # Don't save as SVG if running under Windows.
#
# Plot code goes here.
#
fig.savefig('figure_name.png', dpi = 200)
if platform.system() != 'Windows':
    # In my installations of Windows 7, it takes an inordinate amount of time to save
    # graphs as .svg files, so on that platform I've disabled the call that does so.
    # The first run of a script is still a little slow while everything is loaded in,
    # but execution times of subsequent runs are improved immensely.
    fig.savefig('figure_name.svg')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.