Reputation: 507
I'm trying to plot a 3D figure in Matplotlib with the scale in the offset text. For this purpose, I've used ImportanceOfBeingErnest's custom axis' major formatter in order to have this scale in a latex format:
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import numpy as np
class OOMFormatter(matplotlib.ticker.ScalarFormatter):
def __init__(self, order=0, fformat="%1.1f", offset=True, mathText=True):
self.oom = order
self.fformat = fformat
matplotlib.ticker.ScalarFormatter.__init__(self,useOffset=offset,useMathText=mathText)
def _set_order_of_magnitude(self):
self.orderOfMagnitude = self.oom
def _set_format(self, vmin=None, vmax=None):
self.format = self.fformat
if self._useMathText:
self.format = r'$\mathdefault{%s}$' % self.format
x = np.linspace(0, 22, 23)
y = np.linspace(-10, 10, 21)
X, Y = np.meshgrid(x, y)
V = -(np.cos(X/10)*np.cos(Y/10))**2*1e-4
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
surf = ax.plot_surface(X, Y, V, cmap=cm.jet,
linewidth=0, antialiased=False)
ax.zaxis.set_major_formatter(OOMFormatter(int('{:.2e}'.format(np.min(V)).split('e')[1]), mathText=True))
ax.zaxis.set_rotate_label(False)
ax.set_xlabel(r'$x$ (cm)', size=20, labelpad=10)
ax.set_ylabel(r'$y$ (cm)', size=20, labelpad=10)
ax.set_zlabel(r'$A_z$', size=20, labelpad=10)
This results in the following figure:
Note that the scale (zaxis off-set text, x 10^{-4}) is rotationed 90 degrees. To solve this, I've tried to acess the element of the off-set text and set its rotation to 0:
ax.zaxis.get_offset_text().set_rotation(0)
ax.zaxis.get_offset_text().get_rotation()
>>> 0
Which was of no use, since the off-set text didn't rotate an inch. I've then tried to print the text object when running the plot function:
surf = ax.plot_surface(X, Y, V, cmap=cm.jet,
linewidth=0, antialiased=False)
.
.
.
print(ax.zaxis.get_offset_text())
>>>Text(1, 0, '')
This made me think that perhaps the off-set text wasn't stored inside this variable, but when I run the same command without calling the plot function it returns exactly what I expected it to return:
print(ax.zaxis.get_offset_text())
>>>Text(-0.1039369506424546, 0.050310729257045626, '$\\times\\mathdefault{10^{−4}}\\mathdefault{}$')
What am I doing wrong?
Upvotes: 5
Views: 986
Reputation: 260910
I have to say, this is an excellent and intriguing question and I scratched my head a while over it…
You can access the offset text with ot = ax.zaxis.get_offset_text()
. It is easy to hide it ot.set_visible(False)
, but for some unknown reason it does not work to rotate ot.set_rotation(90)
. I tried to print the text value with print(ot.get_text())
, but this outputs nothing, unless the plot was already drawn. Only after the plot is drawn, it returns '$\\times\\mathdefault{10^{-4}}\\mathdefault{}$'
. This tells me that this is likely the source of the problem. Whatever you apply to the offset text, it gets overwritten in a final step of the graph generation and this fails.
I came to the conclusion that the best approach is to hide the offset and annotate yourself the graph. You can do it programmatically using the following snippet:
ax.zaxis.get_offset_text().set_visible(False)
exponent = int('{:.2e}'.format(np.min(V)).split('e')[1])
ax.text(ax.get_xlim()[1]*1.1, ax.get_ylim()[1], ax.get_zlim()[1],
'$\\times\\mathdefault{10^{%d}}\\mathdefault{}$' % exponent)
Result:
Upvotes: 3