1

A matplotlib secondary axis is reversed when plotting on a log scale.

In the attached example, I use magnitude as the primary (left) axis, luminosity as the secondary (right) axis. Higher luminosity should correspond to a smaller magnitude, as it does in the first two plots. However, in the third plot, when I use a log scale for luminosity, luminosity increases with magnitude, which is incorrect. Is this a bug, or am I doing something wrong?

# Test secondary axis
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
print(matplotlib.__version__)

# Mag to luminosity (solar units) conversion
M_sun = 4.83

def mag2lum(mag):
    return 10**(0.4*(M_sun - mag))

def lum2mag(L):
    return M_sun - 2.5*np.log10(L)

def mag2lgl(mag):
    return 0.4*(M_sun - mag)

def lgl2mag(lgl):
    return M_sun - 2.5*lgl

# log luminosity as second axis - correct behaviour
fig, ax = plt.subplots(constrained_layout=True)
plt.ylabel(r'$M_G$ [mag]')
plt.xlim(-1, 5)
plt.ylim(10, 0)
secax = ax.secondary_yaxis('right', functions=(mag2lgl, lgl2mag))
secax.set_ylabel(r'$\log\ L_G\ [L_\odot]$')
plt.show()

# luminosity as second axis - correct behaviour, but labelling is horrible
fig, ax = plt.subplots(constrained_layout=True)
plt.ylabel(r'$M_G$ [mag]')
plt.xlim(-1, 5)
plt.ylim(10, 0)
secax = ax.secondary_yaxis('right', functions=(mag2lum, lum2mag))
secax.set_ylabel(r'$L_G\ [L_\odot]$')
plt.show()

# luminosity as second axis on log scale: axis is reversed
fig, ax = plt.subplots(constrained_layout=True)
plt.ylabel(r'$M_G$ [mag]')
plt.xlim(-1, 5)
plt.ylim(10, 0)
secax = ax.secondary_yaxis('right', functions=(mag2lum, lum2mag))
secax.set_ylabel(r'$L_G\ [L_\odot]$')
secax.set_yscale('log')
plt.show()

log luminosity as second axis - correct behaviour luminosity as second axis - correct behaviour, but labelling is horrible luminosity as second axis on log scale: axis is reversed

In third plot, luminosity should increase upwards.

1 Answers1

1

In the simple case of one y axis, you can indeed set the scale. However, note what it is happening here, in your third case. The "scale" is not a decision of the plot drawing, but it is a consequence of the functions that translate between the main y axis and the secondary axis. What I mean is that the values and their positions in the secondary axis are "locked", as they need to match the corresponding ones in the main axis. So what you need, is not to try to change the secondary axis, but its ticks and labels. For that, you can use custom tick locations labels, or, as done below, import pre-defined locators and formatters.

import numpy as np
import matplotlib.pyplot as plt

from matplotlib.ticker import LogLocator, LogFormatterMathtext

M_sun = 4.83

def mag2lum(mag):
    return 10**(0.4*(M_sun - mag))

def lum2mag(L):
    return M_sun - 2.5*np.log10(L)

fig, ax = plt.subplots(constrained_layout=True)
ax.set_ylabel(r'$M_G$ [mag]')
ax.set_xlim(-1, 5)
ax.set_ylim(10, 0)
secax = ax.secondary_yaxis('right', functions=(mag2lum, lum2mag))
secax.set_ylabel(r'$L_G\ [L_\odot]$')

# do this instead of trying to set scale
secax.yaxis.set_major_locator(LogLocator())
secax.yaxis.set_major_formatter(LogFormatterMathtext())

plt.show()

enter image description here

fdireito
  • 1,709
  • 1
  • 13
  • 19
  • Further to my answer, notice that I changed some lines from "plt." to their equivalent of "ax.". In your original, your were mixing the two interfaces. You can read about those in: https://matplotlib.org/matplotblog/posts/pyplot-vs-object-oriented-interface/, https://stackoverflow.com/questions/66410803/understanding-of-fig-ax-and-plt-when-combining-matplotlib-and-pandas/66414375#66414375, and https://stackoverflow.com/questions/37970424/what-is-the-difference-between-drawing-plots-using-plot-axes-or-figure-in-matpl. – fdireito Feb 08 '23 at 10:38