Performance and large data

Matplotlib · Expert · 14 min

Plots with millions of points or real-time updates can take seconds per frame or produce huge files unless you know where the time goes.

Rendering cost grows with the number of artists and vertices, so the big wins are drawing fewer, simpler things:

  • Use one artist, not many: one plot call with many points is far faster than thousands of separate calls. For many line segments, use a LineCollection.
  • Prefer `plot(..., "o")` to `scatter` when markers share one size and color.
  • Path simplification: path.simplify (on by default) removes vertices that do not change the rendered line; path.simplify_threshold trades accuracy for speed. agg.path.chunksize (for example 10000) helps Agg with very long lines.
  • Marker subsampling: markevery=10 draws a marker every tenth point.
  • Decimate first: for 10 million samples, aggregate (resample, hexbin, hist2d, datashader) to what the screen can show.

For vector output, rasterized=True on a dense artist embeds it as an image inside the SVG/PDF while text and axes stay vector, which keeps files small; dpi in savefig controls that image's resolution.

Blitting makes live updates fast: draw the static background once, cache it with canvas.copy_from_bbox(fig.bbox), then for each frame restore_region, redraw only the changing artists with ax.draw_artist, and canvas.blit. Mark animated artists with animated=True so the normal draw skips them. FuncAnimation(blit=True) does exactly this.

Measure before optimizing: time fig.canvas.draw() and compare.

Example

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import time, os

rng = np.random.default_rng(0)
x = rng.normal(size=200_000); y = rng.normal(size=200_000)

def timed_draw(fig):
    t = time.perf_counter(); fig.canvas.draw()
    return time.perf_counter() - t

fig1, ax1 = plt.subplots(); ax1.scatter(x, y, s=1)
fig2, ax2 = plt.subplots(); ax2.plot(x, y, ",", linestyle="none")
print(timed_draw(fig2) < timed_draw(fig1))  # True: one Line2D beats a collection

fig3, ax3 = plt.subplots(); ax3.plot(x, y, ",", linestyle="none", rasterized=True)
fig2.savefig("vector.pdf"); fig3.savefig("raster_inside.pdf", dpi=150)
print(os.path.getsize("raster_inside.pdf") < os.path.getsize("vector.pdf"))  # True

Remember

  • Fewer artists and vertices is the main lever; aggregate huge data first
  • rasterized=True keeps vector files small for dense layers
  • Blitting redraws only changing artists over a cached background

Watch out for

  • Calling plot in a Python loop once per point, creating thousands of artists.

Check yourself

  1. A PDF with a 500k-point scatter is 60 MB and slow to open. Which change keeps axes and text sharp but shrinks the file?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: matplotlib.org/stable/users/explain/artists/performance.html

Builds on

Unlocks

Nothing builds on this yet.