เหตุใดการวางแผนด้วย Matplotlib จึงช้ามาก


103

ฉันกำลังประเมินไลบรารีพล็อต python ที่แตกต่างกัน ตอนนี้ฉันกำลังลอง matplotlib และฉันค่อนข้างผิดหวังกับการแสดง ตัวอย่างต่อไปนี้แก้ไขจากตัวอย่าง SciPyและให้ฉันเพียง ~ 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

1
ลองเปลี่ยนแบ็กเอนด์ ดูคำตอบของฉัน: stackoverflow.com/a/30655528/2066079 หรือคำถามที่พบบ่อยเกี่ยวกับแบ็กเอนด์: matplotlib.org/faq/usage_faq.html#what-is-a-backend
dberm22

คำตอบ:


119

ก่อนอื่น (แม้ว่าจะไม่เปลี่ยนแปลงประสิทธิภาพเลยก็ตาม) ให้พิจารณาทำความสะอาดโค้ดของคุณคล้ายกับสิ่งนี้:

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 อาจไม่ใช่ทางเลือกที่ดี มุ่งเน้นไปที่ตัวเลขคุณภาพการตีพิมพ์ไม่ใช่การแสดงผลแบบเรียลไทม์

อย่างไรก็ตามมีหลายสิ่งที่คุณสามารถทำได้เพื่อเร่งตัวอย่างนี้

มีสาเหตุหลักสองประการที่ทำให้สิ่งนี้ช้าอย่างที่เป็นอยู่

1) การโทรfig.canvas.draw()วาดทุกอย่างใหม่ มันเป็นคอขวดของคุณ ในกรณีของคุณคุณไม่จำเป็นต้องวาดสิ่งต่างๆใหม่เช่นขอบเขตแกนป้ายกำกับ ฯลฯ

2) ในกรณีของคุณมีพล็อตย่อยจำนวนมากที่มีป้ายกำกับเครื่องหมายถูกจำนวนมาก สิ่งเหล่านี้ใช้เวลาวาดนาน

ทั้งสองอย่างนี้สามารถแก้ไขได้โดยใช้ blitting

ในการทำ blitting อย่างมีประสิทธิภาพคุณจะต้องใช้รหัสเฉพาะแบ็กเอนด์ ในทางปฏิบัติหากคุณกังวลจริงๆเกี่ยวกับภาพเคลื่อนไหวที่ราบรื่นคุณมักจะฝังแผนภาพ matplotlib ไว้ในชุดเครื่องมือ gui บางประเภทดังนั้นนี่จึงไม่ใช่ปัญหามากนัก

อย่างไรก็ตามหากไม่รู้อีกเล็กน้อยว่าคุณกำลังทำอะไรอยู่ฉันไม่สามารถช่วยคุณได้

อย่างไรก็ตามมีวิธีการทำแบบ Gui-neutral ที่ยังเร็วพอสมควร

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

โค้ดของคุณเร็วมาก แต่สุดท้ายก็มี 2,000 บรรทัดต่อแกน! "line.set_ydata" สร้างบรรทัดใหม่แทนที่จะอัปเดต - หรือว่าพื้นหลังไม่ถูกล้าง? นอกจากนี้ทำไมเวอร์ชันของคุณจึงเร็วกว่ามาก เพียงเพราะคุณทิ้ง "draw ()" และแทนที่ด้วย "ax.draw_artist"?
memyself

ในตัวอย่างใด (ฉันทดสอบแล้ว แต่อาจคัดลอกเวอร์ชันที่ไม่ถูกต้องลงในคำตอบ) นอกจากนี้คุณกำลังใช้ matplotlib เวอร์ชันใด
Joe Kington

4
นี่คือลิงค์ไปยังภาพผลลัพธ์i.imgur.com/aBRFz.pngนี่อาจเป็นสิ่งประดิษฐ์ที่เกิดจากการ์ดแสดงผลของฉันหรือไม่
memyself

7
ฉันเห็นสิ่งเดียวกับที่ตัวเองเห็นใน i.imgur.com/aBRFz.png จนกระทั่งฉันย้ายการจับภาพพื้นหลังด้านล่าง fig.show ()
Michael Browne

4
ดี แต่animationดูเหมือนจะอัปเดตพล็อตตามintervalช่วงเวลาถ้าฉันต้องการอัปเดตเมื่อข้อมูลใหม่พร้อมล่ะ
Alcott

31

Matplotlib สร้างกราฟิกคุณภาพการตีพิมพ์ที่ยอดเยี่ยม แต่ไม่ได้ปรับให้เหมาะสมกับความเร็วมากนัก มีแพ็คเกจการพล็อตไพ ธ อนมากมายที่ออกแบบโดยคำนึงถึงความเร็ว:


1
ฉันสนุกกับpyqtgraph.org/documentationสำหรับข้อมูลสตรีมแบบเรียลไทม์ great job luke
qrtLs

11

ในการเริ่มต้นคำตอบของ Joe Kingtonให้คำแนะนำที่ดีมากโดยใช้แนวทางที่เป็นกลางและคุณควรปฏิบัติตามคำแนะนำของเขา (โดยเฉพาะเกี่ยวกับ Blitting) และนำไปปฏิบัติ ข้อมูลเพิ่มเติมเกี่ยวกับแนวทางนี้โปรดอ่านMatplotlib Cookbook

อย่างไรก็ตามแนวทางที่ไม่ใช่ GUI เป็นกลาง (GUI-biased?) เป็นกุญแจสำคัญในการเร่งการวางแผน กล่าวอีกนัยหนึ่งแบ็กเอนด์มีความสำคัญอย่างยิ่งต่อการวางแผนความเร็ว

ใส่สองบรรทัดนี้ก่อนที่คุณจะนำเข้าสิ่งอื่นจาก matplotlib:

import matplotlib
matplotlib.use('GTKAgg') 

แน่นอนว่ามีตัวเลือกมากมายให้ใช้แทนGTKAggแต่ตามตำราอาหารที่กล่าวมาก่อนหน้านี้เร็วที่สุด ดูลิงก์เกี่ยวกับแบ็กเอนด์สำหรับตัวเลือกเพิ่มเติม


สิ่งนี้ใช้ได้กับ windows เท่านั้น แต่คุณรู้วิธีทำให้มันใช้งานได้บน Mac เหตุผลที่เป็น windows เฉพาะคือ pygtk เป็น windows เฉพาะ
user308827

2
pygtk ไม่ใช่ windows เฉพาะ ในความเป็นจริงมันเป็นความเจ็บปวดอย่างมากในการทำงานภายใต้ 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
คุณควรแก้ไขคำตอบของเขาแทนที่จะโพสต์เป็นคำตอบแยกต่างหาก
endolith

1

สิ่งนี้อาจใช้ไม่ได้กับหลาย ๆ ท่าน แต่โดยปกติแล้วฉันใช้งานคอมพิวเตอร์ภายใต้ Linux ดังนั้นโดยค่าเริ่มต้นฉันจะบันทึกพล็อต matplotlib เป็น PNG และ SVG สิ่งนี้ใช้งานได้ดีภายใต้ Linux แต่ช้ามากในการติดตั้ง Windows 7 ของฉัน [MiKTeX ภายใต้ Python (x, y) หรือ Anaconda] ดังนั้นฉันจึงเพิ่มรหัสนี้และสิ่งต่าง ๆ ก็ทำงานได้ดีอีกครั้ง:

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.