kluonk
kluonk

Reputation: 115

Positioning the exponent of tick labels when using scientific notation

I am looking for a way to change the position of the exponent on an axis when using scientific notation. I got stuck at this problem already a few times. I know already that the default formatter is the ScalarFormatter but it has no option to access the exponent somehow.

There is a workaround but I don't like it, since it also manipulates the existing ticklabels. So far I found out, that the list returned by the axes' get_xmajorticklabels() method contains one text object more if scientific notation is used. For example:

import matplotlib.pyplot as plt
import numpy as np

fig= plt.figure(figsize = plt.figaspect(0.5))
ax1= fig.add_subplot(121)
ax1.plot(np.arange(146), np.random.rand(146))
print(ax1.get_xmajorticklabels())

ax2= fig.add_subplot(122)
ax2.plot(np.arange(146)*1e-6, np.random.rand(146))
print(ax2.get_xmajorticklabels())

Image with the resulting plots

The prints give: <a list of 9 Text xticklabel objects> and <a list of 10 Text xticklabel objects> So I thought the additional list item could be the text object for the exponent. But it's empty when I print the text.

Is there any way to access this exponent as a text object? Then it should be possible to set its position, isn't it?

Upvotes: 6

Views: 12593

Answers (2)

Dusan Kojic
Dusan Kojic

Reputation: 359

My workaround produced this:

import matplotlib.pyplot as plt
from matplotlib import ticker
import numpy as np

fig = plt.figure(figsize = plt.figaspect(0.5))
ax1 = fig.add_subplot(121)
ax1.plot(np.arange(146), 1e+5*np.random.rand(146))
yfmt = ticker.ScalarFormatter(useMathText=True)
yfmt.set_powerlimits((3, 4))
ax1.yaxis.set_major_formatter(yfmt)
ax1.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
ax1.get_yaxis().get_offset_text().set_visible(False)
ax_max = max(ax1.get_yticks())
exponent_axis = np.floor(np.log10(ax_max)).astype(int)
ax1.annotate(r'$\times$10$^{%i}$'%(exponent_axis),
             xy=(.01, .96), xycoords='axes fraction')

ax2 = fig.add_subplot(122)
ax2.plot(np.arange(146)*1e-6, np.random.rand(146))
yfmt = ticker.ScalarFormatter(useMathText=True)
yfmt.set_powerlimits((-6, -5))
ax2.xaxis.set_major_formatter(yfmt)
ax2.ticklabel_format(style='sci', axis='x', scilimits=(0,0))
ax2.get_xaxis().get_offset_text().set_visible(False)
ax_max = max(ax2.get_xticks())
exponent_axis = np.floor(np.log10(ax_max)).astype(int)
ax2.annotate(r'$\times$10$^{%i}$'%(exponent_axis),
             xy=(.89, .01), xycoords='axes fraction')

plt.tight_layout()
plt.show()

Result:

Upvotes: 2

Stephen B
Stephen B

Reputation: 1285

To access the list of Text objects you can use a method of that class, e.g. get_text():

print([s.get_text() for s in ax2.get_xmajorticklabels()])

However, the result of that is

<a list of 9 Text xticklabel objects>
[u'', u'', u'', u'', u'', u'', u'', u'', u'']
<a list of 10 Text xticklabel objects>
[u'', u'', u'', u'', u'', u'', u'', u'', u'', u'']

After running fig.tight_layout(), the output of these Text xticklabel objects can now be enumerated:

<a list of 9 Text xticklabel objects>
[(0.0, 0), (20.0, 0), (40.0, 0), (60.0, 0), (80.0, 0), (100.0, 0), (120.0, 0), (140.0, 0), (160.0, 0)]

<a list of 10 Text xticklabel objects>
[(0.0, 0), (2.0000000000000002e-05, 0), (4.0000000000000003e-05, 0), (6.0000000000000008e-05, 0), (8.0000000000000007e-05, 0), (0.0001, 0), (0.00012000000000000002, 0), (0.00014000000000000001, 0), (0.00016000000000000001, 0), (0, 0)]

For an exponent like -7, there is actually the same amount of objects in both lists.

The closest method I've found for positioning the label is detailed here by Scott. Sadly, it will only work horizontally for the x-axis and vertically for the y-axis, so you can't really arbitrarily position the label on the graph.

ax2.get_xaxis().get_offset_text().set_position((0.5,0))

exponent label moved horizontally

Upvotes: 2

Related Questions