Reputation: 115
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())
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
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()
Upvotes: 2
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))
Upvotes: 2