Customizing Axes and Ticks
The Axes frame - limits, ticks, labels, spines, grid - is where a chart goes from default to deliberate. The defaults (auto-limits, five ticks, black frame on all four sides) are reasonable for exploration and wrong for publication: publication charts usually trim the top and right spines, control the tick set explicitly, and add a quiet grid. This chapter is the toolbox for all of it.
Axis Limits
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 100)
fig, ax = plt.subplots()
ax.plot(x, np.sin(x) + x / 5)
ax.set_xlim(0, 8) # zoom in horizontally
ax.set_ylim(-0.2, 1.6) # fixed vertical window
ax.invert_yaxis() # y grows downward (maps, depth)
plt.show()
Auto-scaling picks "nice" limits around the data; set_xlim/set_ylim override it. Set limits before adding data only if you know the range - otherwise let the first data call establish the frame and then adjust. margins(0.05) adds padding instead of replacing the limits.
Ticks: Values and Labels
import matplotlib.pyplot as plt
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
values = [12, 15, 9, 18, 14, 21]
fig, ax = plt.subplots()
ax.plot(range(len(months)), values, marker="o")
ax.set_xticks(range(len(months)))
ax.set_xticklabels(months)
ax.set_yticks([0, 5, 10, 15, 20])
ax.tick_params(axis="both", which="major", labelsize=10, length=4)
plt.show()
set_xticks places ticks; set_xticklabels renames them (same count, or use a dict-style formatter). tick_params styles length, width, color, and label size for the major and minor tick sets at once. For large numbers, a formatter is cleaner than manual labels:
from matplotlib.ticker import FuncFormatter, MultipleLocator
ax.yaxis.set_major_formatter(FuncFormatter(lambda v, _: f"{v:,.0f}"))
ax.xaxis.set_major_locator(MultipleLocator(5)) # a tick every 5 units
Spines
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([0, 1, 2], [1, 4, 2])
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_color("gray")
ax.spines["bottom"].set_color("gray")
plt.show()
Each side of the frame is a Spine artist addressable by name. Hiding the top and right is the standard "clean" style - it removes the box around the data without losing the reference edges. Move the left spine to x=0 with ax.spines["left"].set_position("zero") for zero-referenced charts.
Grids
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([0, 1, 2, 3], [2, 5, 3, 7])
ax.grid(True, which="both", axis="y",
linestyle=":", linewidth=0.6, alpha=0.6)
plt.show()
Grid options: which="major"/"minor"/"both", axis="x"/"y"/"both", plus any line style. Horizontal-only grids (for time series) are the most useful form; keep them light - the data should be the loudest thing on the page.
Nonlinear Scales
ax.set_xscale("log")- orders of magnitude (revenue growth, file sizes); data must be positive.ax.set_yscale("symlog", linthresh=1)- log behavior with a linear zone around zero.ax.set_aspect("equal")- one unit on x equals one unit on y (geometry, physics plots).ax.set_proj/ transforms - for the unusual cases, covered with annotations.
Summary
Take control of the frame: fix limits where the data range misleads, set explicit ticks with formatters for readability, hide the top and right spines for the clean style, add a quiet major grid, and switch to log scales when the data spans orders of magnitude. Every one of these is a one-line Axes method - the defaults exist so you can start; these exist so you can finish.