Matplotlib coordinates tranformation

2020-07-18 11:57发布

问题:

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)

回答1:

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:

  1. Transform from axes coords to display coords using transAxes.
  2. 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()