A chart without labels and units is a puzzle; these few calls are what makes a plot usable by someone else.
Three calls cover most labelling: ax.set_title("..."), ax.set_xlabel("...") and ax.set_ylabel("..."). Put units in the axis label, for example "Latency (ms)", so readers do not have to guess. ax.set(title=..., xlabel=..., ylabel=...) sets several properties in one call.
A legend names each series. Give each plotting call a label=, then call ax.legend(); only artists with a label appear, and labels starting with an underscore are ignored. Useful options:
loc="upper left"or the default"best", which searches for the emptiest spot (slow with very many points).ncols=2to lay entries out in columns.frameon=Falsefor a lighter look.bbox_to_anchor=(1.02, 1), loc="upper left"to put the legend outside the plot on the right.
For the whole figure, fig.suptitle("...") adds a title above all subplots, and fig.legend() builds one legend from every Axes.
Text accepts a subset of LaTeX-style math between dollar signs: ax.set_xlabel(r"$\alpha$ (rad)"). Use raw strings so backslashes survive.
If labels overlap or get cut off when saving, the fix is layout, not font size: use layout="constrained" when creating the figure, or bbox_inches="tight" when saving.
Example
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
epochs = [1, 2, 3, 4, 5]
fig, ax = plt.subplots(layout="constrained")
ax.plot(epochs, [0.9, 0.6, 0.45, 0.4, 0.38], label="train")
ax.plot(epochs, [0.95, 0.7, 0.6, 0.58, 0.6], label="validation")
ax.plot(epochs, [0.5] * 5, "k:", label="_target") # hidden from legend
ax.set(title="Training loss", xlabel="Epoch", ylabel="Loss")
leg = ax.legend(loc="upper right", frameon=False)
print([t.get_text() for t in leg.get_texts()]) # ['train', 'validation']
print(ax.get_title()) # Training loss
fig.savefig("loss.png")Remember
- Label both axes and include units
- Give each series
label=then callax.legend() - Labels starting with
_are left out of the legend
Watch out for
- Calling
ax.legend()before plotting, or without anylabel=, which gives an empty legend and a warning.