In [11]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn
%matplotlib inline
In this post I want to collect some ideas I had on setting nice labels in matplotlib. In particular, for scientific papers we usually want a label like "time [s]". Then, if the data is very large we may put the exponent next to the units like "time [$10^{-6}$s]", better still is to use an SI prefix e.g "time [$\mu$s]".
Matplotlib
already has useful routines to format the ticks, but it usually puts the exponent somewhere near to the top of the axis. Here is a typical example using the defaults
In [10]:
x = np.linspace(0, 10, 1000)
y = 1e10 * np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()
For a scientific publication we tend to use $\times10^{10}$ or $10^{10}$, this can easily be achieved with the ScalarFormatter. There is an example on the docs, but here is a simpler one:
In [25]:
x = np.linspace(0, 10, 1000)
y = 1e8 + 1e6 * np.sin(x)
fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(mpl.ticker.ScalarFormatter(useMathText=True, useOffset=False))
ax.plot(x, y)
plt.show()
Note there are two options set here, the first useMathText
forces the scientific notation, while the useOffset
stops the offset being used which makes most plots illegible in my opinion
At some point I ran into an issue with the location of where the exponent was positioned, it was overlapping with other subplots. I did the usual trick and went to StackOverflow and was helpfully redirected to this post which by chance was posted the same day. Building on the ideas posted there I have developed the following:
In [2]:
def update_label(old_label, exponent_text):
if exponent_text == "":
return old_label
try:
units = old_label[old_label.index("[") + 1:old_label.rindex("]")]
except ValueError:
units = ""
label = old_label.replace("[{}]".format(units), "")
exponent_text = exponent_text.replace("\\times", "")
return "{} [{} {}]".format(label, exponent_text, units)
def format_label_string_with_exponent(ax, axis='both'):
""" Format the label string with the exponent from the ScalarFormatter """
ax.ticklabel_format(axis=axis, style='sci')
axes_instances = []
if axis in ['x', 'both']:
axes_instances.append(ax.xaxis)
if axis in ['y', 'both']:
axes_instances.append(ax.yaxis)
for ax in axes_instances:
ax.major.formatter._useMathText = True
plt.draw() # Update the text
exponent_text = ax.get_offset_text().get_text()
label = ax.get_label().get_text()
ax.offsetText.set_visible(False)
ax.set_label_text(update_label(label, exponent_text))
Which can be used like this
In [35]:
fig, ax = plt.subplots()
x = np.linspace(0, 1e8, 1000)
y = 1e10 * np.sin(1e-7*x)
ax.plot(x, y)
ax.set_ylabel("amplitude")
ax.set_xlabel("time [s]")
format_label_string_with_exponent(ax, axis='both')
plt.show()
In [41]:
x = np.linspace(0, 10, 1000)
y = 1e10 * np.sin(x)
def plot(xlabel="time [s]", ylabel="amplitude [Watts]", axis="both"):
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_ylabel(xlabel)
ax.set_xlabel(ylabel)
format_label_string_with_exponent(ax, axis=axis)
plt.show()
In [48]:
from IPython.html.widgets import interact
interact(plot, axis=["x", "y", "both"])