Showing and saving
Backends, the blank-file trap, and getting a file worth sending
plt.showfig.savefigbbox_inchesdpiplt.closematplotlib.useWatch it happen
Play it through, or step back and forth yourself.
matplotlib.use("Agg")render to a file, no window neededplt.get_backend()what you are currently using"Agg"servers, CI, and this lab"TkAgg" / "QtAgg"a desktop window"inline"the notebook defaultmatplotlib draws through a backend. An interactive one opens a window; the Agg backend renders straight to a file and needs no display at all — which is what a server, a CI job, and this lab all use.
The idea
A chart you can't get out of Python isn't much use. This is the plumbing: which renderer is running, why show() sometimes does nothing, and how to save a file that doesn't arrive blank or with the labels sliced off.
Backends
matplotlib builds the figure in memory and then hands it to a backend to actually render. Interactive backends (TkAgg, QtAgg, macosx) open a window. Agg renders straight to an image buffer and needs no display at all — which is what a server, a CI job, and this lab all use.
import matplotlib
matplotlib.use("Agg") # before importing pyplot
import matplotlib.pyplot as plt
plt.get_backend() # what you're actually onIf a script works locally and dies on a server with something about no display, this is it: set Agg and it goes away.
show()
plt.show() displays the figure. In a script it blocks until you close the window. In a notebook it's usually unnecessary — figures display on their own. On Agg it does nothing at all, which is why it has no effect in this lab.
And the classic trap: on most interactive backends show() clears the figure, so this writes an empty image:
plt.plot(days, delhi)
plt.show()
plt.savefig("chart.png") # blankIt isn't a broken install, it's the order. Save first, then show.
savefig
fig.savefig("chart.png", dpi=150, bbox_inches="tight")The format comes from the extension. Prefer fig.savefig over plt.savefig: it says which figure, so it can't drift onto the wrong one.
bbox_inches="tight" is close to mandatory. Without it the crop is the figure rectangle, so a rotated tick label or a long y-label gets sliced off the edge of the saved file even though it looked fine on screen. With it, the crop shrinks to fit whatever is actually there.
Size and resolution
figsize is inches, dpi is dots per inch, and their product is the pixel size of the file:
fig = plt.figure(figsize=(4, 3), dpi=150) # 600 x 450 px
fig.get_size_inches() # array([4., 3.])150 dpi for slides and the web, 300 if it's going to print. Note that raising dpi doesn't rescale the text — it stays the same physical size and simply gets sharper.
Which format
- SVG or PDF — vector. Sharp at any zoom, editable afterwards. Use for anything printed or projected.
- PNG at 150–300 dpi — raster. Slides, docs, the web.
- JPEG — no. It's built for photographs and leaves smudges around every line and letter.
transparent=True drops the background rectangle, which is what you want when the chart is going onto a coloured slide. (It's how the charts in this lab sit on a dark page.)
Saving without a file
savefig takes any file-like object, so a web app can render into memory and send the bytes straight back. SVG is text, PNG is bytes:
import io
buf = io.StringIO()
fig.savefig(buf, format="svg")
buf.getvalue()[:5] # '<?xml' — it really is just textClosing figures
pyplot keeps every figure you create in a global registry, so a loop that makes charts and never closes them leaks memory and eventually warns you. Close as you go:
plt.close(fig) # this one
plt.close("all") # every one
plt.get_fignums() # which are still openSee it run
The lesson's code, ready to run and to fiddle with.
Putting the kettle on…
Starting up…
Worked example
not gradedAlready written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.
trydropping bbox_inches and comparing the character count.
Your turn
3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.
Make a figure 4 inches by 3 at 150 dpi, and return its pixel size as a list of two ints, [width, height].
Plot delhi, save the figure as SVG into an io.StringIO buffer instead of a file, and return the first 5 characters of what came out.
Create three figures, close the second one, and return plt.get_fignums() to show which are still open.
