tại sao âm mưu với Matplotlib lại chậm như vậy?


100

Tôi hiện đang đánh giá các thư viện vẽ đồ thị python khác nhau. Hiện tại tôi đang thử matplotlib và tôi khá thất vọng với màn trình diễn. Ví dụ sau đây được sửa đổi từ các ví dụ SciPy và chỉ cho tôi ~ 8 khung hình mỗi giây!

Bất kỳ cách nào để tăng tốc độ này hoặc tôi nên chọn một thư viện vẽ sơ đồ khác?

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)

Thông tin sau có thể có liên quan: stackoverflow.com/questions/5003094/…
NPE

2
@aix - Glumpy chỉ trợ giúp trong ví dụ đó vì anh ấy đang xử lý dữ liệu hình ảnh hiển thị nhanh chóng. Nó sẽ không giúp ích gì trong trường hợp này.
Joe Kington

1
Hãy thử thay đổi chương trình phụ trợ. Xem câu trả lời của tôi: stackoverflow.com/a/30655528/2066079 . hoặc Câu hỏi thường gặp này về phụ trợ: matplotlib.org/faq/usage_faq.html#what-is-a-backend
dberm22 Ngày

Câu trả lời:


115

Trước hết, (mặc dù điều này sẽ không thay đổi hiệu suất), hãy xem xét xóa mã của bạn, tương tự như sau:

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)

Với ví dụ trên, tôi nhận được khoảng 10 khung hình / giây.

Chỉ cần một lưu ý nhanh, tùy thuộc vào trường hợp sử dụng chính xác của bạn, matplotlib có thể không phải là một lựa chọn tuyệt vời. Nó hướng tới các số liệu chất lượng xuất bản, không phải hiển thị thời gian thực.

Tuy nhiên, có rất nhiều điều bạn có thể làm để tăng tốc ví dụ này.

Có hai lý do chính khiến việc này diễn ra chậm như vậy.

1) Gọi điện fig.canvas.draw()vẽ lại mọi thứ . Đó là nút thắt cổ chai của bạn. Trong trường hợp của bạn, bạn không cần phải vẽ lại những thứ như ranh giới trục, đánh dấu nhãn, v.v.

2) Trong trường hợp của bạn, có rất nhiều ô phụ với rất nhiều nhãn đánh dấu. Những thứ này mất nhiều thời gian để vẽ.

Cả hai điều này đều có thể được khắc phục bằng cách sử dụng blting.

Để xóa hiệu quả, bạn sẽ phải sử dụng mã phụ trợ cụ thể. Trong thực tế, nếu bạn thực sự lo lắng về các hình ảnh động mượt mà, bạn thường nhúng các ô matplotlib vào một số loại bộ công cụ gui, vì vậy đây không phải là vấn đề nhiều.

Tuy nhiên, nếu không biết thêm một chút về những gì bạn đang làm, tôi không thể giúp bạn ở đó.

Tuy nhiên, có một cách trung lập để làm điều đó vẫn nhanh hợp lý.

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)

Điều này mang lại cho tôi ~ 200 khung hình / giây.

Để làm cho điều này thuận tiện hơn một chút, có một animationsmô-đun trong các phiên bản gần đây của matplotlib.

Ví dụ:

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

mã của bạn thực sự rất nhanh, tuy nhiên tôi kết thúc với 2000 dòng trên mỗi trục! bằng cách nào đó "line.set_ydata" tạo một dòng mới thay vì cập nhật nó - hay là nền không được xóa? Thêm vào đó, tại sao phiên bản của bạn nhanh hơn rất nhiều? chỉ vì bạn đã bỏ "draw ()" và thay thế nó bằng "ax.draw_artist"?
memyself

Trong ví dụ nào? (Tôi đã kiểm tra chúng, nhưng có thể sao chép sai phiên bản vào câu trả lời.) Ngoài ra, bạn đang sử dụng phiên bản matplotlib nào?
Joe Kington

4
đây là liên kết đến hình ảnh kết quả i.imgur.com/aBRFz.png đây có thể là một phần mềm do cạc đồ họa của tôi gây ra?
memyself

7
Tôi đã nhìn thấy điều tương tự mà bản thân đã thấy trong i.imgur.com/aBRFz.png cho đến khi tôi di chuyển ảnh chụp nền bên dưới hình ảnh fig.show ().
Michael Browne

4
Đẹp, nhưng animationcó vẻ như cập nhật cốt truyện theo intervalthời gian, điều gì sẽ xảy ra nếu tôi chỉ muốn cập nhật nó khi dữ liệu mới đã sẵn sàng?
Alcott

28

Matplotlib tạo ra đồ họa chất lượng xuất bản tuyệt vời, nhưng không được tối ưu hóa tốt cho tốc độ. Có một loạt các gói âm mưu python được thiết kế với tốc độ:


1
tôi hoàn toàn tận hưởng pyqtgraph.org/documentation để có dữ liệu dòng thời gian thực. công việc tuyệt vời luke
qrtLs

11

Để bắt đầu, câu trả lời của Joe Kington đưa ra lời khuyên rất tốt khi sử dụng cách tiếp cận trung lập và bạn chắc chắn nên nghe lời khuyên của anh ấy (đặc biệt là về Blitting) và áp dụng nó vào thực tế. Thông tin thêm về phương pháp này, hãy đọc Sách dạy nấu ăn Matplotlib

Tuy nhiên, cách tiếp cận không trung tính với GUI (GUI-thiên vị?) Là chìa khóa để tăng tốc độ vẽ. Nói cách khác, phần phụ trợ cực kỳ quan trọng đối với tốc độ của cốt truyện.

Đặt hai dòng này trước khi bạn nhập bất kỳ thứ gì khác từ matplotlib:

import matplotlib
matplotlib.use('GTKAgg') 

Tất nhiên, có nhiều tùy chọn khác nhau để sử dụng thay thế GTKAgg, nhưng theo sách dạy nấu ăn đã đề cập trước đây, đây là cách nhanh nhất. Xem liên kết về phụ trợ để có thêm tùy chọn.


Tuy nhiên, tính năng này chỉ hoạt động trên windows, bạn có biết cách làm cho nó hoạt động trên Mac không. Lý do đó là cửa sổ cụ thể là pygtk là cửa sổ cụ thể
user308827

2
pygtk không phải là cửa sổ cụ thể. Trong thực tế, đó là một nỗi đau rất lớn nhận được nó làm việc trong môi trường Windows (nếu nó thậm chí còn có thể, tôi đã từ bỏ.)
Joseph Redfern

7

Đối với giải pháp đầu tiên được đề xuất bởi Joe Kington (.copy_from_bbox & .draw_artist & canvas.blit), tôi phải chụp nền sau dòng fig.canvas.draw (), nếu không nền không có hiệu ứng và tôi nhận được kết quả tương tự như bạn đã đề cập. Nếu bạn đặt nó sau fig.show (), nó vẫn không hoạt động như đề xuất của Michael Browne.

Vì vậy, chỉ cần đặt dòng nền sau 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
bạn chỉ nên chỉnh sửa câu trả lời của anh ấy thay vì đăng dưới dạng một câu riêng biệt
endolith 5/1213

1

Điều này có thể không áp dụng cho nhiều người trong số các bạn, nhưng tôi thường vận hành máy tính của mình bằng Linux, vì vậy theo mặc định, tôi lưu các ô matplotlib của mình dưới dạng PNG và SVG. Điều này hoạt động tốt trong Linux nhưng chậm đến mức không thể chịu nổi trên các bản cài đặt Windows 7 của tôi [MiKTeX trong Python (x, y) hoặc Anaconda], vì vậy tôi đã thực hiện thêm mã này và mọi thứ lại hoạt động tốt ở đó:

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')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.