Adjust exponent text after setting scientific constraints on matplotlib axis

At the moment, if I set the matplotlib y ticcable marks to scientific mode, it gives me an indicator at the top of the y axis of form 1e-5

I would like to tweak this to read r'$\mathregular{10^{-5}}$' so that it prints beautifully.

Here is my sample code:

 # Create a figure and axis fig, ax = plt.subplots() # Plot 100 random points # the y values of which are very small ax.scatter(np.random.rand(100), np.random.rand(100)/100000.0) # Set the y limits appropriately ax.set_ylim(0, 1/100000.0) # Change the y ticklabel format to scientific format ax.ticklabel_format(axis='y', style='sci', scilimits=(-2, 2)) # Get the offset value offset = ax.yaxis.get_offset_text() # Print it out print '1st offset printout: {}'.format(offset) # Run plt.tight_layout() plt.tight_layout() # Print out offset again - you can see the value now! print '2nd offset printout: {}'.format(offset) # Change it to latex format offset.set_text(r'$\mathregular{10^{-5}}$') # Print it out print '3rd offset printout: {}'.format(offset) # Add some text to the middle of the figure just to # check that it isn't the latex format that the problem ax.text(0.5, 0.5/100000.0, r'$\mathregular{10^{-2}}$') # And show the figure plt.show() 

My conclusion is as follows:

 1st offset printout: Text(0,0.5,u'') 2nd offset printout: Text(0,636.933,u'1e\u22125') 3rd offset printout: Text(0,636.933,u'$\\mathregular{10^{-5}}$') 

enter image description here

You can find the code and print the number here .

There are two oddities: one of them is that I cannot overwrite 1e-5 at the top of the y axis (which is the target), and the second is that I need to run plt.tight_layout() to see this unicode value as an offset.

Can someone tell me where I am going wrong?

thanks

EDIT: In the original question, it was not clear that I would like to automatically determine the exponent how it is currently being ticklabel_format . Therefore, instead of passing the string string to the offset text, it should automatically determine this value and adjust the latex string accordingly.

+6
source share
4 answers

Based on @edsmith, answer one possible work that does what I want to do is get the offset text, convert it to a latex line, disable the offset, and add to this line at the top of the axis.

 def format_exponent(ax, axis='y'): # Change the ticklabel format to scientific format ax.ticklabel_format(axis=axis, style='sci', scilimits=(-2, 2)) # Get the appropriate axis if axis == 'y': ax_axis = ax.yaxis x_pos = 0.0 y_pos = 1.0 horizontalalignment='left' verticalalignment='bottom' else: ax_axis = ax.xaxis x_pos = 1.0 y_pos = -0.05 horizontalalignment='right' verticalalignment='top' # Run plt.tight_layout() because otherwise the offset text doesn't update plt.tight_layout() ##### THIS IS A BUG ##### Well, at least it sub-optimal because you might not ##### want to use tight_layout(). If anyone has a better way of ##### ensuring the offset text is updated appropriately ##### please comment! # Get the offset value offset = ax_axis.get_offset_text().get_text() if len(offset) > 0: # Get that exponent value and change it into latex format minus_sign = u'\u2212' expo = np.float(offset.replace(minus_sign, '-').split('e')[-1]) offset_text = r'x$\mathregular{10^{%d}}$' %expo # Turn off the offset text that calculated automatically ax_axis.offsetText.set_visible(False) # Add in a text box at the top of the y axis ax.text(x_pos, y_pos, offset_text, transform=ax.transAxes, horizontalalignment=horizontalalignment, verticalalignment=verticalalignment) return ax 

Note that you can use the position of the offset text by calling pos = ax_axis.get_offset_text().get_position() , but these values ​​are not axial units (they are most likely pixel units - thanks to @EdSmith - and therefore not help a lot). Therefore, I just set the values ​​of x_pos and y_pos depending on the axis we are looking at.

I also wrote a small function to automatically determine the corresponding x and y limits (although I know that matplotlib has many fancy ways to do this).

 def get_min_max(x, pad=0.05): ''' Find min and max values such that all the data lies within 90% of of the axis range ''' r = np.max(x) - np.min(x) x_min = np.min(x) - pad * r x_max = np.max(x) + pad * r return x_min, x_max 

So, to update my example from the question (with a slight change so that both axes need an exponent):

 import matplotlib.pylab as plt import numpy as np # Create a figure and axis fig, ax = plt.subplots() # Plot 100 random points that are very small x = np.random.rand(100)/100000.0 y = np.random.rand(100)/100000.0 ax.scatter(x, y) # Set the x and y limits x_min, x_max = get_min_max(x) ax.set_xlim(x_min, x_max) y_min, y_max = get_min_max(y) ax.set_ylim(y_min, y_max) # Format the exponents nicely ax = format_exponent(ax, axis='x') ax = format_exponent(ax, axis='y') # And show the figure plt.show() 

enter image description here

Currently available here using ipython laptop showing code output.

I hope this helps!

+3
source

You get offset and set the text value, but there seems to be no way to apply it to the axis ... Even calling ax.yaxis.offsetText.set_text(offset) does not update the displayed offset. Work around it to remove the offset text and replace it with brackets on the axis label,

 ax.yaxis.offsetText.set_visible(False) ax.set_ylabel("datalabel " + r'$\left(\mathregular{10^{-5}}\right)$') 

Or replace it with a text box manually, as a minimal example

 import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np # Create a figure and axis fig, ax = plt.subplots() mpl.rc('text', usetex = True) # Plot 100 random points # the y values of which are very small large = 100000.0 x = np.random.rand(100) y = np.random.rand(100)/large ax.scatter(x,y) # Set the y limits appropriately ax.set_ylim(0, 1/large) # Change the y ticklabel format to scientific format ax.ticklabel_format(axis='y', style='sci', scilimits=(-2, 2)) #print(ax.yaxis.offsetText.get_position()) ax.yaxis.offsetText.set_visible(False) ax.text(-0.21, 1.01/large, r'$\mathregular{10^{-2}}$') # And show the figure plt.show() 

I know this is not ideal, but it may be that the offset text cannot be manually changed or can only correspond to numerical values ​​...

+2
source

It seems that plt.ticklabel_format is not working properly. However, if you define ScalarFormatter yourself and set limits for scientific notation for formatting, you can automatically get the offset in mathtext format as follows:

 import matplotlib.pyplot as plt import numpy as np import matplotlib.ticker x = np.linspace(3,5) y = np.sin(np.linspace(0,6*np.pi))*1e5 plt.plot(x,y) mf = matplotlib.ticker.ScalarFormatter(useMathText=True) mf.set_powerlimits((-2,2)) plt.gca().yaxis.set_major_formatter(mf) plt.show() 

enter image description here

+2
source

Add two lines to your code

 import matplotlib.ticker as ptick ax.yaxis.set_major_formatter(ptick.ScalarFormatter(useMathText=True)) 
0
source

All Articles