9

I've spent some time fruitlessly searching for an answer to my question, so I think a new question is in order. Consider this plot:

![enter image description here

The axes labels use scientific notation. On the y-axis, all is well. However, I have tried and failed to get rid off the scaling factor that Python added in the lower-right corner. I would like to either remove this factor completely and simply indicate it by the units in the axis title or have it multiplied to every tick label. Everything would look better than this ugly 1e14.

Here's the code:

import numpy as np data_a = np.loadtxt('exercise_2a.txt')

import matplotlib as mpl 
font = {'family' : 'serif',
        'size'   : 12} 
mpl.rc('font', **font)

import matplotlib.pyplot as plt 
fig = plt.figure() 
subplot = fig.add_subplot(1,1,1)

subplot.plot(data_a[:,0], data_a[:,1], label='$T(t)$', linewidth=2)

subplot.set_yscale('log')               
subplot.set_xlabel("$t[10^{14}s]$",fontsize=14) 
subplot.set_ylabel("$T\,[K]$",fontsize=14) 
plt.xlim(right=max(data_a [:,0])) 
plt.legend(loc='upper right')

plt.savefig('T(t).pdf', bbox_inches='tight')

Update: Incorporating Will's implementation of scientificNotation into my script, the plot now looks like

enter image description here

Much nicer if you ask me. Here's the complete code for anyone wanting to adopt some part of it:

import numpy as np
data = np.loadtxt('file.txt')

import matplotlib as mpl
font = {'family' : 'serif',
        'size'   : 16}
mpl.rc('font', **font)

import matplotlib.pyplot as plt
fig = plt.figure()
subplot = fig.add_subplot(1,1,1)

subplot.plot(data[:,0], data[:,1], label='$T(t)$', linewidth=2)

subplot.set_yscale('log')
subplot.set_xlabel("$t[s]$",fontsize=20)
subplot.set_ylabel("$T\,[K]$",fontsize=20)
plt.xlim(right=max(data [:,0]))
plt.legend(loc='upper right')

def scientificNotation(value):
    if value == 0:
        return '0'
    else:
        e = np.log10(np.abs(value))
        m = np.sign(value) * 10 ** (e - int(e))
        return r'${:.0f} \cdot 10^{{{:d}}}$'.format(m, int(e))

formatter = mpl.ticker.FuncFormatter(lambda x, p: scientificNotation(x))
plt.gca().xaxis.set_major_formatter(formatter)


plt.savefig('T(t).pdf', bbox_inches='tight', transparent=True)
Janosh
  • 3,392
  • 2
  • 27
  • 35

3 Answers3

6

Just divide the x-values by 1e14:

subplot.plot(data_a[:,0] / 1e14, data_a[:,1], label='$T(t)$', linewidth=2)

If you want to add the label to each individual tick, you'll have to provide a custom formatter, like in tom's answer.

If you want it to look like as nice as the ticks on your y-axis, you could provide a function to format it with LaTeX:

def scientificNotation(value):
    if value == 0:
        return '0'
    else:
        e = np.log10(np.abs(value))
        m = np.sign(value) * 10 ** (e - int(e))
        return r'${:.0f} \times 10^{{{:d}}}$'.format(m, int(e))

# x is the tick value; p is the position on the axes.
formatter = mpl.ticker.FuncFormatter(lambda x, p: scientificNotation(x))
plt.gca().xaxis.set_major_formatter(formatter)

Of course, this will clutter your x-axis up quite a bit, so you might end up needing to display them at an angle, for example.

Will Vousden
  • 32,488
  • 9
  • 84
  • 95
  • Thanks for the tip. I briefly tried that previously and thought it didn't work, because the plot disappeared and the scale factor remained. I only started using Python yesterday so I assumed it was one of many syntax mistakes made since then. But now that you brought it up too, I checked again and realized that it only went wrong the first time because I had forgotten to also add the rescaling to `put.xlim`, as in `plt.xlim(right=max(data_a [:,0])/1e14)`. – Janosh Oct 29 '15 at 14:28
  • Do you also know of a way to have the factor appear in every tick label? That would mean much less fiddling around in case the order of magnitude of the axis labels changes. – Janosh Oct 29 '15 at 14:30
  • @Casimir: yep, you need to set the `formatter`. See my answer – tmdavison Oct 29 '15 at 15:50
  • @Will Thanks a lot for your edit. This looks exactly like what I had in mind. I added a plot made with your implementation of `scientificNotation` to my question. Personally, I don't think the x-axis looks cluttered. – Janosh Oct 30 '15 at 08:04
  • More of a scientific point than a Python point - I like this answer, but I would suggest leaving the numbers on the axis as 0, 1, 2, 3, 4, 5 and changing the axis title to t / 1e14s. The number 1 in this case is literally t = 1x10e14 s / 10e14 s. Similarly the number 10^4 on the y axis is T = 1e4 K / K. Units in square brackets make no sense - you are dividing by the units. – Dave Dec 06 '21 at 12:48
4

You can also change the tick formatter with the ticker module.

An example would be to use a FormatStrFormatter:

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

fig,ax = plt.subplots()
ax.semilogy(np.linspace(0,5e14,50),np.logspace(3,7,50),'b-')
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%.0e'))

enter image description here

Also see the answers here with lots of good ideas for ways to solve this.

Community
  • 1
  • 1
tmdavison
  • 64,360
  • 12
  • 187
  • 165
2

In addition to the good answer from Will Vousden, you can set what you write in your ticks with:

plt.xticks(range(6), range(6))

the first range(6) is the location and the second is the label.

jrjc
  • 21,103
  • 9
  • 64
  • 78