Skip to content

inset_locator.inset_axes produces axes without extent and at incorrect position. #8952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ImportanceOfBeingErnest opened this issue Jul 28, 2017 · 4 comments

Comments

@ImportanceOfBeingErnest
Copy link
Member

ImportanceOfBeingErnest commented Jul 28, 2017

The mpl_toolkits.axes_grid1.inset_locator.inset_axes is not working as expected when using the bbox_to_anchor argument. Using inset_axes(ax, width="70%", height="30%",bbox_to_anchor=(0.4,0.1), loc=3) gives an inset axes without extention at an incorrect position. (This issue is raised in this stackoverflow post.)

Complete example:

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
plt.rcParams["figure.figsize"] = 4,3

fig, ax= plt.subplots()

iax =inset_axes(ax, width="70%", height="30%", bbox_to_anchor=(0.4,0.1), loc=3)

iax.plot([1,2,4])
plt.show()

gives

image

and a warning

c:\winpython\winpython-64bit-2.7.10.3\python-2.7.10.amd64\lib\site-packages\matplotlib\axis.py:1035: UserWarning: Unable to find pixel distance along axis for interval padding of ticks; assuming no interval padding needed.

when being run with python 2.7, matplotlib 2.0.2. In the lower left corner of the image you see what I suppose are the ticks of the zero-sized axes. Since the documentation clearly states that width and height can be strings (and they actually can be if bbox_to_anchor is ommited) and since the default bbox_transform is parent_axes.transAxes, the above is clearly expected to work.

Note that when explicitely supplying the bbox_transform we at least get the axes at the correct position,

iax =inset_axes(ax, width="70%", height="30%", bbox_to_anchor=(0.4,0.1), 
                         loc=3, bbox_transform=ax.transAxes)

image

while width and height are further ignored.

Is this an error in the documentation or a bug in the matplotlib code?

The expected outcome of the above would be an inset axes with the lower left corner at (0.4,0.1) in axes coordinates and a width of 0.3 and a height of 0.7 (also in normalized axes units) as shown in the below image (which is produced by other means than inset_axes):

image

@tacaswell tacaswell added this to the 2.2 (next next feature release) milestone Jul 28, 2017
@tacaswell
Copy link
Member

Likely both, the axis_grid tools are both under documented and under tested.

@alcrene
Copy link

alcrene commented Apr 1, 2018

I've spent a bit of time trying to understand where this behaviour is coming from. I've noted the following:

  • When bbox_to_anchor is provided, the display coordinates are used instead of the axes coordinates. As noted, this can be remedied by providing the bbox_transform argument.
  • One must provide four arguments to bbox_to_anchor (left, bottom, width, height).
  • With width and height arguments set to 1, behaviour is pretty much as expected. The effect of setting to other values is difficult to understand.

The following snippet illustrates the current behaviour on matplotlib 2.1.0, which may be useful to anyone trying to work around or fix it.

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

mpl.transforms.DEBUG = True

xdata, ydata = np.arange(8), np.arange(8)

plt.figure(figsize=(6,6))
# Using all default arguments, things seems OK.
ax = plt.subplot(421)
ax.set_title("Defaults")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="30%", height="30%", loc='upper left')

# The following fails with "ValueError: Transforming to a singular bounding box.",
# even though according to the docstring it is valid.
#ax = plt.subplot(422)
#ax.set_title("bbox=(0,0)")
#ax.plot(xdata, ydata)
#axins = inset_axes(ax, width="30%", height="30%", loc='upper left',
#                   bbox_to_anchor=(0, 0))


# According to the docstring, the default value for `bbox_transform` should
# be `ax.transAxes`, and yet the following uses display coordinates.
ax = plt.subplot(423)
ax.set_title("bbox=(0,0,1,1), default transform")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="30%", height="30%", loc='upper left',
                   bbox_to_anchor=(0, 0, 1, 1))  # Tiny axes in lower left of figure
                   #bbox_to_anchor=(100, 70, 100, 100)) # These coords put the
                                                        # inset within the plot

# Supplying `ax.transAxes` as argument recovers most of the expected behaviour
ax = plt.subplot(424)
ax.set_title("bbox=(0,0,1,1)")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="30%", height="30%", loc='upper left',
                   bbox_to_anchor=(0, 0, 1, 1),
                   bbox_transform=ax.transAxes)

# The effect of changing the bbox width and height is difficult to predict
ax = plt.subplot(425)
ax.set_title("bbox=(0, 0, 0.5, 1)")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="30%", height="30%", loc='upper left',
                   bbox_to_anchor=(0, 0, .5, 1),
                   bbox_transform=ax.transAxes)
ax = plt.subplot(426)
ax.set_title("bbox=(0, 0, 1, 0.5)")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="30%", height="30%", loc='upper left',
                   bbox_to_anchor=(0, 0, 1, .5),
                   bbox_transform=ax.transAxes)

# `loc` argument applies to *both* the parent and inset axes simultaneously
# i.e. with 'upper right', coordinates are those of the upper right point
# of the inset, relative to the upper right of the parent axes.
ax = plt.subplot(427)
ax.set_title("bbox=(0,0,1,1), upper right")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="30%", height="30%", loc='upper right',
                   bbox_to_anchor=(0, 0, 1, 1),
                   bbox_transform=ax.transAxes)
ax = plt.subplot(428)
ax.set_title("bbox=(0,0,1,1), lower right")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="30%", height="30%", loc='lower right',
                   bbox_to_anchor=(0, 0, 1, 1),
                   bbox_transform=ax.transAxes)

plt.subplots_adjust(hspace=0.8)

mpltoolkits_inset_behaviour

@ImportanceOfBeingErnest
Copy link
Member Author

Wow, good catch @alcrene. 👍
I think this now all makes sense. Even width and height are consistent. E.g. the following are two ways to specify the same inset. Either you make the width/height some percentage or you make the box width and height fractional. (You just have to mind that the position is different due to the loc then).

plt.figure(figsize=(6,3))
ax = plt.subplot(221)
ax.set_title("100%, (0.5,1-0.3,.3,.3)")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="100%", height="100%", loc='upper left',
                   bbox_to_anchor=(0.5,1-0.3,.3,.3), bbox_transform=ax.transAxes)


ax = plt.subplot(222)
ax.set_title("30%, (0.5,0,1,1)")
ax.plot(xdata, ydata)
axins = inset_axes(ax, width="30%", height="30%", loc='upper left',
                   bbox_to_anchor=(0.5,0,1,1), bbox_transform=ax.transAxes)

image

The bbox_transform not being the axes transform has by now been "fixed" in #10756. But that fix apparently broke the usual behaviour of the inset_axes taking numbers as arguments for width and height.

@jklymak jklymak modified the milestones: needs sorting, v3.0 Apr 2, 2018
@tacaswell tacaswell modified the milestones: v3.0, v3.1 Aug 11, 2018
@ImportanceOfBeingErnest
Copy link
Member Author

Closing since this is now fixed by #11060.

@QuLogic QuLogic modified the milestones: v3.1, v2.2.3 Aug 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants