Search code examples
pythonpython-3.xmatplotlibplotxticks

matplotlib - replace xtick labels values with log format by others label values


I have the following code snippet to do a plot with logscale for x-axis :

# Number of values for loop
num_prior_loop = 100
# Declare prior array
prior_fish = np.zeros(num_prior_loop)
prior_start = 0
prior_end = 3
prior_fish = np.logspace(prior_start,prior_end,num_prior_loop)**2
# Declare FoM final array
FoM_final = np.zeros(num_prior_loop)
...

# Plot
fig, ax = plt.subplots()    
ax.plot(prior_fish, FoM_final)
#ax.set_xticks([np.linspace(prior_start,prior_end)])
#ax.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter())
plt.xlabel(r'Prior on 5 ratios '+r'$\alpha = b_{sp}/b_{ph}$'+' in Fisher GCsp', fontsize=10)
plt.ylabel('FoM')
plt.title('FoM vs Prior - Prior on both GCsp and XC', fontsize=10)
plt.savefig('FoM_vs_Prior_alpha_on_both_GCsp_and_XC.pdf')

that produces the following figure :

figure

Now, I would like to do a modification, that is, I want the xtick 10^0 up to 10^6 to be automatically replaced by 1, 10^-1, 10^-2, ...., 10^-6.

I say automatically since the start and end of the 2 bounds (1 and 10^-6) may vary.

UPDATE 1: I tried the solution suggested by @max in answer below but it fails :

Here what I got :

figure wrong

If I do :

for txt in axs.get_xticklabels():
    # get position
    x,y = txt.get_position()
    print('x,y =', x,y)
    # value/formatting
    val = txt.get_text()
    # create new label
    if x == 1 or x == 0:
        val_new = f'${round(x)}$'
    else:
        val_new = '$\\mathdefault{10^{' + f'{round(np.log10(1/x))}' + '}}$'
    # append
    xTickLabel.append( Text(x,y,val_new) )
# set labels
axs.set_xticklabels( xTickLabel )
# Plot and save
plt.plot(prior_fish, FoM_final)
plt.draw()
plt.savefig('FoM_vs_Prior_alpha_on_both_GCsp_and_XC.png')

I get only zeros :

x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0
x,y = 0 0

and printing val returns always empty value.

Here the entire code :

# Plot
fig, axs = plt.subplots()
axs.set_xscale('log')
plt.draw()

plt.xlabel(r'Prior on 5 ratios '+r'$\alpha = b_{sp}/b_{ph}$'+' in Fisher GCsp', fontsize=10)
plt.ylabel('FoM')
plt.title('FoM vs Prior - Prior on both GCsp and XC', fontsize=10)
# create new labels
xTickLabel = []
for txt in axs.get_xticklabels():
    # get position
    x,y = txt.get_position()
    # value/formatting
    val = txt.get_text()
    # create new label
    if x == 1 or x == 0:
        val_new = f'${round(x)}$'
    else:
        val_new = '$\\mathdefault{10^{' + f'{round(np.log10(1/x))}' + '}}$'
    # append
    xTickLabel.append( Text(x,y,val_new) )
# set labels
axs.set_xticklabels( xTickLabel )
# Plot and save
plt.plot(prior_fish, FoM_final)
plt.draw()
plt.savefig('FoM_vs_Prior.pdf')

Where is the bug ? How to get values different values of "0" and "1" in order to avoid to be in the case

   if x == 1 or x == 0:
        val_new = f'${round(x)}$'

Solution

  • It seems that you want to modify the text-labels directly. Have a look at the code below. We get the position of the ticks, invert the x-value, and change the matplotlib.text.Text-object (or rather, I create new ones). The trick is that the positions remain the same; we just change the text that is displayed at this position/tick-label:

    from matplotlib import pyplot as plt
    from matplotlib.text import Text
    
    import numpy as np
    
    fig, axs = plt.subplots(2,1)
    # first axis
    axs[0].set_xscale('log')
    axs[0].set_yscale('log')
    axs[0].scatter(2**np.arange(10), 2**np.arange(10))
    plt.draw()
    
    # second axis
    axs[1].set_xscale('log')
    axs[1].set_yscale('log')
    axs[1].scatter(2**np.arange(10), 2**np.arange(10))
    
    
    # create new labels
    xTickLabel = []
    for txt in axs[0].get_xticklabels():
        # get position
        x,y = txt.get_position()
        # value/formatting
        val = txt.get_text()
        # create new label
        if x == 1 or x == 0:
            val_new = f'${round(x)}$'
        else:
            val_new = '$\\mathdefault{10^{' + f'{round(np.log10(1/x))}' + '}}$'
        # append
        xTickLabel.append( Text(x,y,val_new) )
    # set labels
    axs[1].set_xticklabels( xTickLabel )
    
    plt.draw()
    

    output