Search code examples
pythonmatplotlibaxis-labels

matplotlib format_major_ticks not retrieving axes tick labels


Using Python, I am trying to get the tick labels of an axis that spans many orders of magnitude to be displayed in scientific notation, which should be relatively straight forward with: ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.2E')) (full code below)

but this seems to ignore the tick labels that were previously set, and instead formats range(len(axis_vector)) in scientific notation as shown in Figure 1. The correct tick labels but not displayed in scientific notation (by not using ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.2E'))) are shown in Figure 2.

Figure 1:

Figure 1

Figure 2:

Figure 2

How can I get it to display the actual tick labels in scientific notation?

Here is the code with randomly generated sample data:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import matplotlib.ticker as mtick

y = np.array([1E0,5E0,1E1,5E1,1E2,5E2,1E3,5E3,1E4,5e4,1E5,5E5,1E6,5E5,1E7,5E7])
x = np.arange(0.2,3.2,0.2) 

### generate sample data with random powers of 10 
data = np.zeros(240)

for i in range(len(data)):

    power = np.round(np.random.rand()*10)

    data[i] = 10**power    

image = data.reshape((16,15))

### Plotting 
fig = plt.figure(figsize=(6,6))
ax1 = fig.add_subplot(111)

im = ax1.imshow(image, interpolation='none',norm=LogNorm(vmin=(np.min(image)),vmax=(np.max(image))))

ax1.set_xticks(np.arange(len(x)), minor=False)
ax1.set_xticklabels(x, minor=False)
ax1.set_xlabel('x-axis')

ax1.set_yticks(np.arange(len(y)), minor=False)
ax1.set_yticklabels(y, minor=False)
ax1.set_ylabel('y-axis')

ax1.tick_params(labelbottom='on',labeltop='off', labelleft='on',labelright='off', 
    top='off', right='off')

ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.2E'))  

axcolor = fig.add_axes([0.9, 0.12, 0.03, 0.79])
t = np.logspace(1,11,num=12)
fig.colorbar(im, cax=axcolor, ticks=t, format='%.3E')

Solution

  • The issue is because the tick formatter operates directly on the tick values not the previous labels. In your case the y tick values are at np.arange(len(x))

    In your code, where you passed an array of numbers to the set_yticklabels method which internally just got the string representation of each value in the array and used that

    labels = [str(x) for x in y]
    

    What you'll want to do is to instead create your custom (formatted) ytick labels and assign those using set_yticklabels

    labels = ['%.2E' % x for x in y]
    ax1.set_yticklabels(labels)
    

    If you wanted to be fancy, you could use your FormatStrFormatter object to do this as well

    formatter = mtick.FormatStrFormatter('%.2E')
    labels = [formatter(x) for x in y]
    ax1.set_yticklabels(labels)
    

    enter image description here