I am trying to understand this code snippet:
def add_inset(ax, rect, *args, **kwargs):
box = ax.get_position()
inax_position = ax.transAxes.transform(rect[0:2])
infig_position = ax.figure.transFigure.inverted().transform(inax_position)
new_rect = list(infig_position) + [box.width * rect[2], box.height * rect[3]]
return fig.add_axes(new_rect, *args, **kwargs)
This code adds an inset to an existing figure. It looks like this:
The original code is from this notebook file.
I don't understand why two coordinates transformation are needed:
inax_position = ax.transAxes.transform(rect[0:2])
infig_position = ax.figure.transFigure.inverted().transform(inax_position)
Explanation
In the method add_inset(ax, rect)
, rect
is a rectangle in axes coordinates. That makes sense because you often want to specify the location of the inset relavtive to the axes in which it lives.
However in order to later be able to create a new axes, the axes position needs to be known in figure coordinates, which can then be given to fig.add_axes(figurecoordinates)
.
So what is needed is a coordinate transform from axes coordinates to figure coordinates. This is performed here in a two-step process:
- Transform from axes coords to display coords using
transAxes
.
- Transform from display coords to figure coords using the inverse of
transFigure
.
This two step procedure could be further condensed in a single transform like
mytrans = ax.transAxes + ax.figure.transFigure.inverted()
infig_position = mytrans.transform(rect[0:2])
It may be of interest to read the matplotlib transformation tutorial on how transformations work.
Alternatives
The above might not be the most obvious method to place an inset. Matplotlib provides some tools itself. A convenient method is the mpl_toolkits.axes_grid1.inset_locator
. Below are two ways to use its inset_axes
method when creating insets in axes coordinates.
import matplotlib.pyplot as plt
import mpl_toolkits.axes_grid1.inset_locator as il
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(4,4))
ax1.plot([1,2,3],[2.2,2,3])
# set the inset at upper left (loc=2) with width, height=0.5,0.4
axins = il.inset_axes(ax1, "50%", "40%", loc=2, borderpad=1)
axins.scatter([1,2,3],[3,2,3])
# set the inset at 0.2,0.5, with width, height=0.8,0.4
# in parent axes coordinates
axins2 = il.inset_axes(ax2, "100%", "100%", loc=3, borderpad=0,
bbox_to_anchor=(0.2,0.5,0.7,0.4),bbox_transform=ax2.transAxes,)
axins2.scatter([1,2,3],[3,2,3])
plt.show()