почему заговор с Matplotlib так медленно?



в настоящее время я оцениваю различные библиотеки построения 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)
737   5  

5 ответов:

во-первых, (хотя это не изменит производительность вообще) рассмотрите возможность очистки вашего кода, аналогично этому:

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.

чтобы сделать блиттинг эффективно, вам придется использовать бэкэнд-специфичный код. На практике, если вы действительно беспокоитесь о плавной анимации, вы обычно встраиваете графики matplotlib в какой-то набор инструментов gui, так что это не большая проблема.

однако, не зная немного больше о том, что вы делаете, я не могу вам помочь.

тем не менее, есть 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()

Matplotlib делает отличную графику качества публикации, но не очень хорошо оптимизирован для скорости. Существует множество пакетов построения графиков python, которые разработаны с учетом скорости:

Для начала ответ Джо Кингтона дает очень хороший совет, используя gui-нейтральный подход, и вы обязательно должны принять его совет (особенно о Блиттинге) и применить его на практике. Подробнее об этом подходе читайте в Matplotlib Поваренная Книга

правда, без графического интерфейса-нейтральный (ГИП-предвзято?) подход является ключом к ускорению построения. Другими словами,backend чрезвычайно важно построить скорость.

положить эти две строки, прежде чем импортировать что-либо еще из matplotlib:

import matplotlib
matplotlib.use('GTKAgg') 

конечно, есть различные варианты, чтобы использовать вместо GTKAgg, но согласно поваренной книге, упомянутой ранее, это было самым быстрым. См. ссылку о бэкэндах для получения дополнительных опций.

для первого решения, предложенного Джо Кингтон ( .copy_from_bbox & .draw_artist & canvas.blit), я должен был захватить фон после рис.холст.нарисуйте () линию, иначе фон не имел никакого эффекта, и я получил тот же результат, что и вы упомянули. Если поставить его после фиг.show () он по-прежнему не работает, как предложил Майкл Браун.

Так что просто поместите фоновую строку после в холст.ничья():

[...]
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]

это может не относиться ко многим из вас, но я обычно работаю на своих компьютерах под 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')

Comments

    Ничего не найдено.