Skip to content

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

Closed

Description

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

Activity

  1. added this to the milestone on Jul 28, 2017
  2. tacaswell commented on Jul 28, 2017

    @tacaswell
    Member

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

  3. alcrene commented on Apr 1, 2018

    @alcrene

    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

  4. ImportanceOfBeingErnest commented on Apr 1, 2018

    @ImportanceOfBeingErnest
    MemberAuthor

    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.

  5. modified the milestones: , on Apr 2, 2018
  6. modified the milestones: v3.0, v3.1 on Aug 11, 2018
  7. ImportanceOfBeingErnest commented on Aug 11, 2018

    @ImportanceOfBeingErnest
    MemberAuthor

    Closing since this is now fixed by #11060.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions