Skip to content

legend is not present in the generated image if I use 'tight' for bbox_inches #10194

Closed

Description

@jdhao

summary of the problem

I tried to put legend outside of the axes. If I use fig.legend() method to produce legend and use bbox_inches='tight' in savefig() method. The legend is not present in the produced image (using the argument bbox_extra_artists also does not work). If I use ax.legend() instead, the legend is present in the saved image.

Code for reproduction

import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl

x = np.arange(-5, 5, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)

y3 = np.sin(2*x)
y4 = np.cos(2*x)

fig, (ax1, ax2) = plt.subplots(ncols=1, nrows=2, figsize=(10, 6))

ax1.plot(x, y1, label='sin')
ax1.plot(x, y2, label='cos')

ax2.plot(x, y3, label='sin')
ax2.plot(x, y4, label='cos')

handles, labels = ax1.get_legend_handles_labels()
# following code does not work
legend = fig.legend(handles, labels, loc='lower left', ncol=2, frameon=False,
              bbox_to_anchor=(0.12, 0.88))
plt.savefig('test.jpg', bbox_extra_artists=[legend,], bbox_inches='tight')

# following code works
# legend = ax1.legend(handles, labels, ncol=2, frameon=False,
#                     loc='lower left', bbox_to_anchor=(-0.01, 1.2))
# plt.savefig('test.jpg', bbox_inches='tight')
plt.show()

outcome

The saved images are different if I use different legend method.

using fig.legend method

image

using ax.legend method

image

Matplotlib version

  • Operating system: Windows 8.1 and Linux (CentOS 7) (This problem is maybe platform independent)
  • Matplotlib version: 2.0.2(Linux), 2.0.0 (Windows)
  • Matplotlib backend: Qt5Agg
  • Python version: 3.5.2 (Windows), 3.6.2 (Linux)

The matplotlib and python are all distributed with Anaconda.

quesiton

Is this a bug or something I do not know. Maybe there is difference between fig.legend and ax.legend method?

Activity

  1. added this to the milestone on Jan 8, 2018
  2. tacaswell commented on Jan 8, 2018

    @tacaswell
    Member

    Suspect that this is a bug in the tight bbox code which is not taking the figure legend into account.

  3. jdhao commented on Jan 8, 2018

    @jdhao
    Author

    Thanks, right now, I will just stick to the ax.legend method.

  4. anntzer commented on Jan 8, 2018

    @anntzer
    Contributor

    Probably the fix will be similar to #9164...

  5. added a commit that references this issue on Mar 15, 2018
  6. dstansby commented on Apr 21, 2019

    @dstansby
    Member

    Odd, the bbox caclulation is including the legend, but the legend just isn't being drawn. This is obvious to see if one does bbox_to_anchor=(0.12, 1.5):
    test

  7. tacaswell commented on Apr 21, 2019

    @tacaswell
    Member

    I think the bug is that the legend is, under the hood, drawn with an OffsetBox subclass which does not properly take into account the changed bounding box so draws its self outside of the active area.

    If you change it to bbox_to_anchor=(0.5, .5) then you do see the legend both on the screen and in the jpg, but they are in different places in relation to the axes.

  8. danijar commented on Sep 12, 2019

    @danijar

    Curious what's the status of this? Is there a workaround besides placing the legend in a different location?

  9. ImportanceOfBeingErnest commented on Sep 12, 2019

    @ImportanceOfBeingErnest
    Member

    The next step here would be to find out why this happens for a legend, but not for an AnchoredOffsetbox. Both should be pretty similar, so any difference can already lead to a practical solution.

  10. Vast-Stars commented on Dec 2, 2019

    @Vast-Stars

    The same problem trapped me for hours.

  11. samuelmiver commented on Feb 6, 2020

    @samuelmiver

    Same problem

  12. tacaswell commented on Feb 6, 2020

    @tacaswell
    Member

    This needs someone to dig into the two classes and sort out why they are behaving differently. It is very likely that something is being cached that should not be (which is why the the legend moves around).

  13. jklymak commented on May 25, 2020

    @jklymak
    Member

    Right, so the issue is that somehow the offset for the box is being calculated relative to the current figures origin rather than the original figures origin. Still not quite sure why, but you can
    easily see this by setting bbox_inches=mtransforms.Bbox.from_extents([1, 0, 3, 3] or some such and see it move up or to the right.

  14. jklymak commented on May 26, 2020

    @jklymak
    Member

    bbox_inches works by re-defining transFigure to put the origin up and to the right to be tight with the axes decorations. However, so far as I can see, legend and offsetBbox do not participate in the transform logic. So the offset in legend is relative the the bottom left of the current figure, and not transformed.

    I have a fix, but I thinkt the proper fix is to fix offsetBox to actually have a transform. But I find the architecture of offsetBox extremely hard to understand, so I'm not even sure if that is possible.

  15. QuLogic commented on May 26, 2020

    @QuLogic
    Member

    I spent too long last night trying to figure this out. This seems to only occur with bbox_to_anchor, because then the offset is determined by:

    bbox = Bbox.from_bounds(0, 0, width, height)

    which uses width and height from VPacket.get_extent_offsets, and somewhere in there I guess is where the problem starts happening, but it was too roundabout to figure out just yet.

  16. jklymak commented on May 26, 2020

    @jklymak
    Member

    As noted in #17511, what happens is that legend does

           if transform is None:
                transform = BboxTransformTo(self.parent.bbox)
    

    bbox_tight.py never changes the parent bbox, but rather directly changes transFigure. So the legend never hears that it should move relative to the bottom left, and stays in the same spot.

    The fix in #17511 is to explicitly set the default bbox_transform=fig.transFigure for figure.legend. There is really no reason for it to be generic (as a default).

  17. amir-rahnama commented on Nov 11, 2023

    @amir-rahnama

    This problem is showing up again in version 3.1.2 and Python 3.7 and 3.8 btw.

  18. jklymak commented on Nov 11, 2023

    @jklymak
    Member

    @amir-rahnama please open a new reproducible issue. The code above works fine for me.

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