Matplotlib, альтернативы savefig () для повышения производительности при сохранении в объект CString? - PullRequest
16 голосов
/ 22 марта 2011

Я пытаюсь ускорить процесс сохранения своих диаграмм в изображениях.Прямо сейчас я создаю объект cString, куда я сохраняю диаграмму, используя savefig;но я очень, очень признателен за любую помощь, чтобы улучшить этот метод сохранения изображения.Я должен делать эту операцию десятки раз, а команда savefig очень и очень медленная;должен быть лучший способ сделать это.Я читал кое-что о сохранении его в виде несжатого необработанного изображения, но понятия не имею, как это сделать.Меня не волнует, могу ли я переключиться на другой более быстрый бэкэнд.

, то есть:

RAM = cStringIO.StringIO()

CHART = plt.figure(.... 
**code for creating my chart**

CHART.savefig(RAM, format='png')

Я использовал matplotlib с бэкэндом FigureCanvasAgg.

Спасибо!

Ответы [ 2 ]

33 голосов
/ 22 марта 2011

Если вам нужен только необработанный буфер, попробуйте fig.canvas.print_rgb, fig.canvas.print_raw и т. Д. (Разница между ними в том, что raw - это rgba, тогда как rgb - это rgb. Также есть print_png, * 1006. * и т. д.)

При этом будет использоваться fig.dpi вместо значения по умолчанию для точек на дюйм для savefig (100 точек на дюйм). Тем не менее, даже при сравнении fig.canvas.print_raw(f) и fig.savefig(f, format='raw', dpi=fig.dpi) версия print_canvas является незначительно быстрее незначительно быстрее, так как не приводит к сбросу цвета патча оси и т. Д., Что savefig делает по умолчанию.

Однако, несмотря на это, большую часть времени, затрачиваемого на сохранение фигуры в необработанном формате, составляет просто рисование фигуры, которую невозможно обойти.

Во всяком случае, в качестве бессмысленного, но забавного примера рассмотрим следующее:

import matplotlib.pyplot as plt
import numpy as np
import cStringIO

plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    fig.canvas.draw()

Brownian walk animation

Если мы посмотрим на время необработанного розыгрыша:

import matplotlib.pyplot as plt
import numpy as np
import cStringIO

fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    fig.canvas.draw()

На моем компьютере это занимает ~ 25 секунд.

Если вместо этого мы выгружаем необработанный буфер RGBA в буфер cStringIO, он на самом деле незначительно быстрее ~ 22 секунды (это верно только потому, что я использую интерактивный бэкэнд! В противном случае это будет эквивалентно.):

import matplotlib.pyplot as plt
import numpy as np
import cStringIO

fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    ram = cStringIO.StringIO()
    fig.canvas.print_raw(ram)
    ram.close()

Если мы сравним это с использованием savefig, со сравнительно установленным dpi:

import matplotlib.pyplot as plt
import numpy as np
import cStringIO

fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    ram = cStringIO.StringIO()
    fig.savefig(ram, format='raw', dpi=fig.dpi)
    ram.close()

Это занимает ~ 23,5 секунды. По сути, savefig просто устанавливает некоторые параметры по умолчанию и вызывает print_raw, в этом случае, так что разница очень мала.

Теперь, если мы сравним необработанный формат изображения с форматом сжатого изображения (png), мы увидим гораздо более существенную разницу:

import matplotlib.pyplot as plt
import numpy as np
import cStringIO

fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)

for i in xrange(1000):
    xy = np.random.random(2*num).reshape(num,2) - 0.5
    offsets = scat.get_offsets() + 0.3 * xy
    offsets.clip(0, max_dim, offsets)
    scat.set_offsets(offsets)
    scat._sizes += 30 * (np.random.random(num) - 0.5)
    scat._sizes.clip(1, 300, scat._sizes)
    ram = cStringIO.StringIO()
    fig.canvas.print_png(ram)
    ram.close()

Это занимает ~ 52 секунды! Очевидно, что при сжатии изображения возникают большие затраты.

Во всяком случае, это, вероятно, излишне сложный пример ... Я думаю, я просто хотел избежать реальной работы ...

2 голосов
/ 03 июля 2012

Мне также нужно было быстро создать много графиков. Я обнаружил, что многопроцессорность улучшила скорость печати с количеством доступных ядер. Например, если 100 графиков заняли 10 секунд в одном процессе, то для разделения задачи на 4 ядра потребовалось ~ 3 секунды.

...