pythonmatplotliblogarithmscientific-notationticker

Custom ticks label in scientific notation while using log scale


I'm having trouble with matplotlib (version 3.1.3) : I would like to add custom ticks and tick labels on a log scale axis while preserving scientific notation.

To say it otherwise: I want to add custom ticks on a log scale axis and label them using the good old-fashioned '%1.1e' (or whatever numbers) format but, for instance, instead of having '2.5e-02', I would like to have '2.5 x 10^-2' (or '2.5 \times 10^{-2}' in latex).

So I start with a minimum working piece of code without custom ticks:

import matplotlib as mpl
import matplotlib.pyplot as plt
print('MATPLOTLIB  VERSION : %s' % mpl.__version__)

plt.style.use("default")

# DATA
x = [0.1, 0.075, 0.05, 0.025, 0.01, 0.0075, 0.005, 0.0025, 0.001, 0.00075, 0.0005, 0.00025, 0.0001, 7.5e-05, 5e-05, 2.5e-05, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09, 1e-10]
y = x

fig = plt.figure()
ax = plt.axes()
plt.loglog()
plt.minorticks_off()
path = ax.plot(x, y)

plt.savefig('test.png')

which gives:

enter image description here

Nice but, as I said, I would like to add custom ticks on the xaxis. More precisely, I would like to put limits to the axis and define equally log-spaced labels between those limits. Let's say I want 4 ticks; it gives the following piece of code:

import matplotlib as mpl
import matplotlib.pyplot as plt
print('MATPLOTLIB  VERSION : %s' % mpl.__version__)

plt.style.use("default")

# DATA
x = [0.1, 0.075, 0.05, 0.025, 0.01, 0.0075, 0.005, 0.0025, 0.001, 0.00075, 0.0005, 0.00025, 0.0001, 7.5e-05, 5e-05, 2.5e-05, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09, 1e-10]
y = x

xmin = min(x)
xmax = max(x)
ymin = min(y)
ymax = max(y)

# XTICKS
nbdiv = 4
xTicks = []
k = pow((xmin/xmax),1./(nbdiv-1.))
for i in range(0,nbdiv):
  xTicks.append(xmax*pow(k,i))

# PLOT
fig = plt.figure()
ax = plt.axes()
plt.loglog()
plt.minorticks_off()
plt.axis([xmin,xmax,ymin,ymax])
plt.xticks(xTicks)
path = ax.plot(x, y)

plt.savefig('test_working_4.png')

which provides the following plot:

enter image description here

That's kind of the result I wanted to obtain. However, if the number of ticks (nbdiv) changes, for instance becomes equal to 5, I get:

enter image description here

And this time only the first and last ticks are labelled. It appears that only numbers which are equal (or at least close) to an integer power of ten (10^n) are labelled. I tried to change the default tick format with a matplot.ticker.ScalarFormatter but I didn't manage to tune it to solve this problem. I also tried LogFormatterMathText and LogFormatterSciNotation, it wasn't better.

The issue in itself doesn't seem so difficult to me, so I don't understand why I'm having so much trouble... What am I doing wrong? Does someone has keys to make me understand my errors?

In any case, I thank for reading and I thank you in advance for your response.

P.S.: Sorry for potential english mistakes, it's not my native language.


Solution

  • Solved, based on your code above. This one is much simpler. You need to use xticklabels.

    %matplotlib inline
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    from sympy import pretty_print as pp, latex
    print('MATPLOTLIB  VERSION : %s' % mpl.__version__)
    
    plt.style.use("default")
    
    # DATA
    x = [0.1, 0.075, 0.05, 0.025, 0.01, 0.0075, 0.005, 0.0025, 0.001, 0.00075, 0.0005, 0.00025, 0.0001, 7.5e-05, 5e-05, 2.5e-05, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09, 1e-10]
    y = x
    
    xmin = min(x)
    xmax = max(x)
    ymin = min(y)
    ymax = max(y)
    
    # XTICKS
    nbdiv = 5
    xTicks = []
    xticklabels = []
    k = pow((xmin/xmax),1./(nbdiv-1.))
    for i in range(0,nbdiv):
      xTicks.append(xmax*pow(k,i))
      printstr = '{:.2e}'.format(xmax*pow(k,i))
      ls = printstr.split('e')
      xticklabels.append((ls[0]+' x $10^{'  +ls[1] + '}$'))
    
    # PLOT
    fig = plt.figure()
    ax = plt.axes()
    plt.loglog()
    plt.minorticks_off()
    plt.axis([xmin,xmax,ymin,ymax])
    plt.xticks(xTicks)
    path = ax.plot(x, y)
    
    plt.savefig('test_working_4.png')
    ax.set_xticklabels(xticklabels)
    

    enter image description here