Skip to content

Secondary Axes

Secondary axes display an alternative scale or transformation of the same data, useful for showing different units or transformations.

secondary_xaxis / secondary_yaxis

Unlike twinx()/twiny() which create independent axes for different data, secondary axes transform the same data to a different scale.

Basic Usage

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()

x = np.linspace(0, 100, 100)
y = np.sin(x * np.pi / 50)

ax.plot(x, y)
ax.set_xlabel('Distance (km)')

# Secondary axis showing same data in miles
def km_to_miles(x):
    return x * 0.621371

def miles_to_km(x):
    return x / 0.621371

secax = ax.secondary_xaxis('top', functions=(km_to_miles, miles_to_km))
secax.set_xlabel('Distance (miles)')

plt.show()

Temperature Conversion

fig, ax = plt.subplots()

temp_c = np.linspace(-40, 100, 100)
y = np.exp(-temp_c / 50)

ax.plot(temp_c, y)
ax.set_xlabel('Temperature (°C)')
ax.set_ylabel('Value')

# Secondary axis in Fahrenheit
def c_to_f(x):
    return x * 9/5 + 32

def f_to_c(x):
    return (x - 32) * 5/9

secax = ax.secondary_xaxis('top', functions=(c_to_f, f_to_c))
secax.set_xlabel('Temperature (°F)')

plt.show()

Secondary Y-Axis

fig, ax = plt.subplots()

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

ax.plot(x, y)
ax.set_ylabel('Energy (Joules)')

# Secondary axis showing same energy in calories
def j_to_cal(x):
    return x / 4.184

def cal_to_j(x):
    return x * 4.184

secax = ax.secondary_yaxis('right', functions=(j_to_cal, cal_to_j))
secax.set_ylabel('Energy (calories)')

plt.show()

Logarithmic Secondary Axis

fig, ax = plt.subplots()

x = np.linspace(1, 100, 100)
ax.plot(x, x ** 2)
ax.set_xlabel('Frequency (Hz)')
ax.set_yscale('log')

# Secondary axis in decades
def hz_to_decades(x):
    return np.log10(x)

def decades_to_hz(x):
    return 10 ** x

secax = ax.secondary_xaxis('top', functions=(hz_to_decades, decades_to_hz))
secax.set_xlabel('Frequency (decades)')

plt.show()

Comparison: Secondary vs Twin Axes

Feature Secondary Axes Twin Axes
Data Same data, different units Different data
Scaling Mathematical transformation Independent
Method secondary_xaxis() twinx()
Alignment Automatically aligned Manual alignment
Use case Unit conversion Overlay different variables

When to Use Each

Secondary Axes: - Same measurement in different units (km ↔ miles) - Same scale with transformation (linear ↔ log) - Wavelength and frequency

Twin Axes: - Price and volume on same chart - Temperature and humidity - Different physical quantities

Practical Examples

1. Wavelength and Frequency

fig, ax = plt.subplots()

wavelength = np.linspace(400, 700, 100)  # nm
intensity = np.exp(-((wavelength - 550) ** 2) / 5000)

ax.plot(wavelength, intensity)
ax.set_xlabel('Wavelength (nm)')
ax.set_ylabel('Intensity')

# Frequency = c / wavelength
c = 3e8  # m/s

def wavelength_to_freq(wl):
    return c / (wl * 1e-9) / 1e12  # THz

def freq_to_wavelength(f):
    return c / (f * 1e12) / 1e-9  # nm

secax = ax.secondary_xaxis('top', functions=(wavelength_to_freq, freq_to_wavelength))
secax.set_xlabel('Frequency (THz)')

plt.show()

2. Pressure Units

fig, ax = plt.subplots()

altitude = np.linspace(0, 50000, 100)  # meters
pressure = 101325 * np.exp(-altitude / 8500)  # Pa

ax.plot(altitude / 1000, pressure / 1000)
ax.set_xlabel('Altitude (km)')
ax.set_ylabel('Pressure (kPa)')

# Secondary in atmospheres
def kpa_to_atm(x):
    return x / 101.325

def atm_to_kpa(x):
    return x * 101.325

secax = ax.secondary_yaxis('right', functions=(kpa_to_atm, atm_to_kpa))
secax.set_ylabel('Pressure (atm)')

plt.show()

3. Date and Day Number

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import numpy as np
from datetime import datetime, timedelta

fig, ax = plt.subplots()

# Data for one year
start = datetime(2024, 1, 1)
days = np.arange(0, 365)
dates = [start + timedelta(days=int(d)) for d in days]
values = np.sin(days * 2 * np.pi / 365)

ax.plot(days, values)
ax.set_xlabel('Day of Year')
ax.set_ylabel('Value')

plt.show()

Key Parameters

Parameter Description
location 'top', 'bottom', 'left', or 'right'
functions Tuple of (forward, inverse) functions
transform Alternative to functions

Common Pitfalls

1. Inverse Function Required

# Both forward AND inverse functions needed
def forward(x):
    return x * 2

def inverse(x):
    return x / 2

# CORRECT
secax = ax.secondary_xaxis('top', functions=(forward, inverse))

# WRONG: Missing inverse
# secax = ax.secondary_xaxis('top', functions=(forward,))

2. Non-Monotonic Transforms

Secondary axes work best with monotonic transformations. Non-monotonic transforms may produce unexpected results.

3. Log Scale Interactions

When using log scales, ensure transformations handle the log space correctly.