mengapa merencanakan dengan Matplotlib sangat lambat?


102

Saya sedang mengevaluasi pustaka plotting python yang berbeda. Saat ini saya mencoba matplotlib dan saya cukup kecewa dengan performanya. Contoh berikut dimodifikasi dari contoh SciPy dan memberi saya hanya ~ 8 frame per detik!

Adakah cara untuk mempercepat ini atau haruskah saya memilih perpustakaan plot yang berbeda?

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)

Berikut ini mungkin relevan: stackoverflow.com/questions/5003094/…
NPE

2
@aix - Glumpy hanya membantu dalam contoh itu karena dia berurusan dengan menampilkan data gambar dengan cepat. Ini tidak akan membantu dalam kasus ini.
Joe Kington

1
Coba ubah backend. Lihat jawaban saya: stackoverflow.com/a/30655528/2066079 . atau FAQ ini tentang backend: matplotlib.org/faq/usage_faq.html#what-is-a-backend
dberm22

Jawaban:


115

Pertama, (meskipun ini tidak akan mengubah kinerja sama sekali) pertimbangkan untuk membersihkan kode Anda, seperti ini:

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)

Dengan contoh di atas, saya mendapatkan sekitar 10fps.

Hanya catatan singkat, tergantung pada kasus penggunaan Anda, matplotlib mungkin bukan pilihan yang bagus. Ini berorientasi pada figur berkualitas publikasi, bukan tampilan waktu nyata.

Namun, ada banyak hal yang dapat Anda lakukan untuk mempercepat contoh ini.

Ada dua alasan utama mengapa ini berjalan lambat.

1) Memanggil fig.canvas.draw()menggambar ulang segalanya . Ini hambatan Anda. Dalam kasus Anda, Anda tidak perlu menggambar ulang hal-hal seperti batas sumbu, label centang, dll.

2) Dalam kasus Anda, ada banyak subplot dengan banyak label centang. Ini membutuhkan waktu lama untuk menggambar.

Keduanya dapat diperbaiki dengan menggunakan blitting.

Untuk melakukan blitting secara efisien, Anda harus menggunakan kode khusus backend. Dalam praktiknya, jika Anda benar-benar khawatir tentang animasi yang mulus, Anda biasanya menyematkan plot matplotlib di semacam toolkit gui, jadi ini bukan masalah.

Namun, tanpa mengetahui lebih banyak tentang apa yang Anda lakukan, saya tidak dapat membantu Anda di sana.

Meskipun demikian, ada cara netral gui untuk melakukannya yang masih cukup cepat.

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)

Ini memberi saya ~ 200fps.

Untuk membuatnya sedikit lebih nyaman, ada animationsmodul dalam versi terbaru matplotlib.

Sebagai contoh:

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

kode Anda memang sangat cepat, namun saya berakhir dengan 2000 baris per sumbu! entah bagaimana "line.set_ydata" membuat baris baru alih-alih memperbaruinya - atau apakah latar belakangnya tidak dihapus? Plus, mengapa versi Anda jauh lebih cepat? hanya karena Anda menjatuhkan "draw ()" dan menggantinya dengan "ax.draw_artist"?
memyself

Dalam contoh yang mana? (Saya mengujinya, tetapi mungkin menyalin-tempel versi yang salah ke dalam jawaban.) Juga, versi matplotlib mana yang Anda gunakan?
Joe Kington

4
berikut ini tautan ke gambar yang dihasilkan i.imgur.com/aBRFz.png mungkinkah ini artefak yang disebabkan oleh kartu grafis saya?
memyself

7
Saya melihat hal yang sama yang dilihat oleh diri saya sendiri di i.imgur.com/aBRFz.png sampai saya memindahkan tangkapan latar belakang di bawah fig.show ().
Michael Browne

4
Bagus, tetapi animationsepertinya memperbarui plot berdasarkan intervalperiode waktu, bagaimana jika saya hanya ingin memperbaruinya ketika data baru sudah siap?
Alcott

29

Matplotlib membuat grafik kualitas publikasi yang bagus, tetapi tidak dioptimalkan dengan baik untuk kecepatan. Ada berbagai paket plotting python yang dirancang dengan mempertimbangkan kecepatan:


1
Saya sangat menikmati pyqtgraph.org/documentation untuk data aliran waktu nyata. kerja bagus luke
qrtLs

11

Untuk memulai, jawaban Joe Kington memberikan saran yang sangat baik dengan menggunakan pendekatan netral-gui, dan Anda harus mengikuti nasihatnya (terutama tentang Blitting) dan mempraktikkannya. Info lebih lanjut tentang pendekatan ini, baca Buku Masak Matplotlib

Namun, pendekatan non-GUI-netral (GUI-bias?) Adalah kunci untuk mempercepat plotting. Dengan kata lain, backend sangat penting untuk kecepatan plot.

Letakkan dua baris ini sebelum Anda mengimpor apa pun dari matplotlib:

import matplotlib
matplotlib.use('GTKAgg') 

Tentu saja, ada berbagai pilihan untuk digunakan sebagai pengganti GTKAgg, tetapi menurut buku resep yang disebutkan sebelumnya, ini adalah yang tercepat. Lihat tautan tentang backend untuk opsi lebih lanjut.


Ini hanya berfungsi di windows, apakah Anda tahu cara membuatnya berfungsi di Mac. Alasan khusus untuk windows adalah karena pygtk adalah windows khusus
user308827

2
pygtk tidak spesifik untuk windows. Faktanya, sangat menyakitkan membuatnya bekerja di bawah Windows (jika mungkin, saya sudah menyerah.)
Joseph Redfern

7

Untuk solusi pertama yang diusulkan oleh Joe Kington (.copy_from_bbox & .draw_artist & canvas.blit), saya harus menangkap latar belakang setelah garis fig.canvas.draw (), jika tidak, latar belakang tidak berpengaruh dan saya mendapatkan hasil yang sama seperti Anda sebutkan. Jika Anda meletakkannya setelah fig.show (), itu masih tidak berfungsi seperti yang diusulkan oleh Michael Browne.

Jadi letakkan saja garis latar belakang setelah 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
Anda hanya perlu mengedit jawabannya alih-alih memposting sebagai jawaban terpisah
endolit

1

Ini mungkin tidak berlaku untuk banyak dari Anda, tetapi saya biasanya mengoperasikan komputer saya di Linux, jadi secara default saya menyimpan plot matplotlib saya sebagai PNG dan SVG. Ini berfungsi dengan baik di Linux tetapi sangat lambat pada instalasi Windows 7 saya [MiKTeX dengan Python (x, y) atau Anaconda], jadi saya telah menambahkan kode ini, dan semuanya berfungsi dengan baik di sana lagi:

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')
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.