Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is plotting with Matplotlib so slow?

People also ask

Is matplotlib better than Plotly?

Matplotlib is also a great place for new Python users to start their data visualization education, because each plot element is declared explicitly in a logical manner. Plotly, on the other hand, is a more sophisticated data visualization tool that is better suited for creating elaborate plots more efficiently.

Is Pyqtgraph faster than matplotlib?

matplotlib: For plotting, pyqtgraph is not nearly as complete/mature as matplotlib, but runs much faster.

What dpi should I use for matplotlib?

The default value for dpi in matplotlib. pyplot. figure() function is 100.


First off, (though this won't change the performance at all) consider cleaning up your code, similar to this:

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)

With the above example, I get around 10fps.

Just a quick note, depending on your exact use case, matplotlib may not be a great choice. It's oriented towards publication-quality figures, not real-time display.

However, there are a lot of things you can do to speed this example up.

There are two main reasons why this is as slow as it is.

1) Calling fig.canvas.draw() redraws everything. It's your bottleneck. In your case, you don't need to re-draw things like the axes boundaries, tick labels, etc.

2) In your case, there are a lot of subplots with a lot of tick labels. These take a long time to draw.

Both these can be fixed by using blitting.

To do blitting efficiently, you'll have to use backend-specific code. In practice, if you're really worried about smooth animations, you're usually embedding matplotlib plots in some sort of gui toolkit, anyway, so this isn't much of an issue.

However, without knowing a bit more about what you're doing, I can't help you there.

Nonetheless, there is a gui-neutral way of doing it that is still reasonably fast.

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)

This gives me ~200fps.

To make this a bit more convenient, there's an animations module in recent versions of matplotlib.

As an example:

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 makes great publication-quality graphics, but is not very well optimized for speed. There are a variety of python plotting packages that are designed with speed in mind:

  • http://vispy.org
  • http://pyqtgraph.org/
  • http://docs.enthought.com/chaco/
  • http://pyqwt.sourceforge.net/
    [ edit: pyqwt is no longer maintained; the previous maintainer is recommending pyqtgraph ]
  • http://code.google.com/p/guiqwt/

To start, Joe Kington's answer provides very good advice using a gui-neutral approach, and you should definitely take his advice (especially about Blitting) and put it into practice. More info on this approach, read the Matplotlib Cookbook

However, the non-GUI-neutral (GUI-biased?) approach is key to speeding up the plotting. In other words, the backend is extremely important to plot speed.

Put these two lines before you import anything else from matplotlib:

import matplotlib
matplotlib.use('GTKAgg') 

Of course, there are various options to use instead of GTKAgg, but according to the cookbook mentioned before, this was the fastest. See the link about backends for more options.


For the first solution proposed by Joe Kington ( .copy_from_bbox & .draw_artist & canvas.blit), I had to capture the backgrounds after the fig.canvas.draw() line, otherwise the background had no effect and I got the same result as you mentioned. If you put it after the fig.show() it still does not work as proposed by Michael Browne.

So just put the background line after the 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]

This may not apply to many of you, but I'm usually operating my computers under Linux, so by default I save my matplotlib plots as PNG and SVG. This works fine under Linux but is unbearably slow on my Windows 7 installations [MiKTeX under Python(x,y) or Anaconda], so I've taken to adding this code, and things work fine over there again:

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