Skip to content

Spine Customization

Spines are the lines that form the borders of the plotting area. This document covers spine basics, visibility control, and positioning.

What are Spines?

A plot has four spines:

  • 'top': Upper border
  • 'bottom': Lower border (x-axis line)
  • 'left': Left border (y-axis line)
  • 'right': Right border

Access spines through the ax.spines dictionary:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(12, 3))
ax.plot(x, y)

print(type(ax.spines['top']))  # <class 'matplotlib.spines.Spine'>
print(ax.spines.keys())        # ['left', 'right', 'bottom', 'top']

plt.show()

Each spine is a Spine object with methods for customization:

spine = ax.spines['bottom']
# Available methods: set_visible, set_color, set_position, set_linewidth, etc.

Spine Visibility

set_visible

Hide or show individual spines:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(12, 3))
ax.plot(x, y)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.show()

set_color to 'none'

An alternative way to hide spines:

ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')

Common Visibility Patterns

Clean two-spine (L-shape):

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

Bottom only:

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)

No spines:

for spine in ax.spines.values():
    spine.set_visible(False)

set_linewidth

Control spine thickness:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots()
ax.plot(x, y)

ax.spines['bottom'].set_linewidth(2)
ax.spines['left'].set_linewidth(2)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

plt.show()

set_color

Change spine colors:

ax.spines['bottom'].set_color('blue')
ax.spines['left'].set_color('blue')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

Iterating Over Spines

Apply changes to all spines:

for spine in ax.spines.values():
    spine.set_linewidth(2)
    spine.set_color('gray')

Spine Position

set_position

Move a spine to a specific location:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(12, 3))
ax.plot(x, y)
ax.spines['bottom'].set_position('zero')
ax.spines['left'].set_position('zero')
plt.show()

Position Types

Position Type Description Example
'zero' At data coordinate 0 set_position('zero')
'center' At axes center set_position('center')
('data', value) At specific data coordinate set_position(('data', -15))
('axes', fraction) Fraction of axes (0 to 1) set_position(('axes', 0.5))
('outward', points) Outward from data area set_position(('outward', 10))

Centered Axes (Math Style)

Create a coordinate system centered at the origin:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(12, 3))
ax.plot(x, y)

# Hide top and right spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

# Move bottom and left to zero
ax.spines['bottom'].set_position('zero')
ax.spines['left'].set_position('zero')

plt.show()

set_bounds

Limit the extent of a spine:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 2, 100)
y1 = x**3 + 5*x**2 + 10
y2 = 3*x**2 + 10*x
y3 = 6*x + 10

fig, ax = plt.subplots()
ax.plot(x, y1, color="blue", label="y(x)", lw=2)
ax.plot(x, y2, color="red", label="y'(x)", lw=2)
ax.plot(x, y3, color="green", label='y"(x)', lw=2)

ax.axhline(0, color='k', lw=1)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_xticks([-4, -2, 0, 2])
ax.set_yticks([-10, 0, 10, 20, 30])

# Position and bound spines
ax.spines['bottom'].set_position(('data', -15))
ax.spines['left'].set_bounds(low=-15, high=41)
ax.spines['right'].set_bounds(low=-15, high=41)

ax.legend(ncol=3, loc=2, bbox_to_anchor=(0, 1), frameon=False)
plt.show()

Arrow-Style Axis

Create arrows at the end of axes:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-3, 3, 100)
y = x ** 2

fig, ax = plt.subplots()
ax.plot(x, y)

# Position spines at zero
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

# Add arrows
ax.plot(1, 0, ">k", transform=ax.get_yaxis_transform(), clip_on=False)
ax.plot(0, 1, "^k", transform=ax.get_xaxis_transform(), clip_on=False)

plt.show()

Complete Example: Mathematical Function Plot

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(x, y, 'b-', lw=2)

# Set up ticks
ax.set_yticks([-1, 0, 1])
ax.set_xticks([-2*np.pi, -np.pi, 0, np.pi, 2*np.pi])
ax.set_xticklabels(['$-2\\pi$', '$-\\pi$', '0', '$\\pi$', '$2\\pi$'])

# Minor ticks
ax.set_xticks(np.linspace(-2*np.pi, 2*np.pi, 17), minor=True)

# Configure spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_position('zero')
ax.spines['left'].set_position('zero')

ax.set_title('$y = \\sin(x)$', pad=20)
plt.show()

Common Spine Configurations

Configuration Code Use Case
Two-spine (clean) Hide top/right Most plots
Centered axes Position at 'zero' Math functions
No spines Hide all Heatmaps, images
Bottom only Hide top/right/left Bar charts

Images with No Spines

For images, hide all spines and ticks:

import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.datasets import fetch_olivetti_faces

faces = fetch_olivetti_faces().images

fig, ax = plt.subplots(5, 5, figsize=(5, 5))
fig.subplots_adjust(hspace=0, wspace=0)

for i in range(5):
    for j in range(5):
        ax[i, j].xaxis.set_major_locator(mpl.ticker.NullLocator())
        ax[i, j].yaxis.set_major_locator(mpl.ticker.NullLocator())
        ax[i, j].imshow(faces[10 * i + j], cmap="bone")

plt.show()

Key Takeaways

  • Four spines: top, bottom, left, right
  • Access via ax.spines['name'] or ax.spines.values()
  • set_visible(False) hides a spine
  • set_position('zero') moves spine to data coordinate 0
  • set_bounds(low, high) limits spine extent
  • Removing top/right spines is a common clean style