Skip to content

Increased padding using tight_layout with pad=0 #7806

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
pfigliozzi opened this issue Jan 12, 2017 · 29 comments
Closed

Increased padding using tight_layout with pad=0 #7806

pfigliozzi opened this issue Jan 12, 2017 · 29 comments
Labels
status: inactive Marked by the “Stale” Github Action

Comments

@pfigliozzi
Copy link

I have found that in the rc2 version of matplotlib that there is extra padding when using plt.tight_layout(pad=0) for axis labels. It also is worse for when using usetex=True for the axis label.

import matplotlib
import matplotlib.pyplot as plt
matplotlib.rcParams['xtick.direction'] = 'in'
matplotlib.rcParams['ytick.direction'] = 'in'
matplotlib.rcParams['xtick.top'] = True
matplotlib.rcParams['ytick.right'] = True
fig = plt.figure(figsize=(3,4))
plt.xticks([0.2,0.8])
plt.yticks([0.2,0.8])
plt.xlabel('x label')
plt.ylabel('y label', usetex=True)
plt.tight_layout(pad=0)

fig.savefig('test.png', dpi=1000)

I ran the above code twice, once in 1.5.1 and again in 2.0.0rc2 (for 1.5.1 you must comment out matplotlib.rcParams['xtick.top'] = True and matplotlib.rcParams['ytick.top'] = True). The screenshot below shows the saved png for 1.5.1 on the left and 2.0.0rc2 on the right:

alt text

In 1.5.1 you can see that the text for both the "y label" and "x label" are right next to the edge of the figure. However in 2.0.0rc2 the x and y labels are significantly spaced from the edge of the figure and the spacing is even worse for the y label where usetex=True.

I am using Python 2.7 installed on Windows from anaconda.

@chrisrapson
Copy link

Is this worth anything to you?
fig.savefig('test.png', dpi=1000, bbox_inches='tight', pad_inches=0)
I'm not sure whether it would be considered a work-around, or whether the developers intended it this way. My most common use case is to save the figures and then use them within another document. I find it easier to estimate spacing if they are tightly cropped, so I would always set pad_inches=0. Equally, I can see that other people with other use cases would be happy with the default (0.1).

Or are you more interested in why plt.tight_layout(pad=0) doesn't give you the expected result?

@pfigliozzi
Copy link
Author

I was interested in why plt.tight_layout(pad=0) doesn't give exactly the same result in 1.5.1 and 2.0. I can't get the text labels all the way against the edge of the figure. I don't know if something has changed with the padding around each text object in 2.0 versus 1.5.1 or how it is handled with plt.tight_layout so I don't know if it was intentional.

Setting pad_inches=0 doesn't seem to change anything in the case I represented above.

@chrisrapson
Copy link

chrisrapson commented Aug 30, 2017

pad_inches is only effective with the bbox_inches='tight' option. Here is a comparison of two images generated in matplotlib2.0 with and without these options:
tight_loose
I'll admit, I can't see any difference on the xlabel, but there is a visible difference on the ylabel.

plt.tight_layout(pad=0) does seem to crop the image tight to the bboxes, so the issue seems to be the padding of the bbox itself, which is set to a default of 4 (in units of font points) in text.py. The commit which set this default seems to be this one:
7829262

You can visualise and set the bbox padding with something like this
plt.xlabel('x label', bbox=dict(facecolor='none', edgecolor='red', pad=10))

@ArchangeGabriel
Copy link
Contributor

So, I think I’ve stumbled upon this issue too. As long as I don’t put labels, plt.tight_layout(pad=0) does the right job. However, with labels (and title as well) they are remaining whitespaces in some cases.

I’m going to post about several cases I’ve tried, in hope this helps understanding what is going wrong.

@ArchangeGabriel
Copy link
Contributor

So first let’s use the default example from here (with some small modifications):

plt.xticks([0.2,0.8])
plt.yticks([0.2,0.8])
plt.xlabel('x label', bbox=dict(facecolor='none', edgecolor='red'))
plt.ylabel('y label', bbox=dict(facecolor='none', edgecolor='red'))
plt.title('Title', bbox=dict(facecolor='none', edgecolor='red'))
plt.tight_layout(pad=0)
plt.savefig('tight_default.pdf')
plt.xticks([0.2,0.8])
plt.yticks([0.2,0.8])
plt.xlabel('x label', bbox=dict(facecolor='none', edgecolor='red', pad=0))
plt.ylabel('y label', bbox=dict(facecolor='none', edgecolor='red', pad=0))
plt.title('Title', bbox=dict(facecolor='none', edgecolor='red',pad=0))
plt.tight_layout(pad=0)
plt.savefig('tight_zero.pdf')

tight_default.pdf
tight_zero.pdf
As you can see, it seems there is only an issue with the x label, and it is not related to bbox padding.

@ArchangeGabriel
Copy link
Contributor

Note btw that constrained_layout gives the same results here (replacing the tight_layout command by adding fig = plt.figure(constrained_layout=True); fig.set_constrained_layout_pads(w_pad=0, h_pad=0) at the beginning):
constrained_default.pdf
constrained_zero.pdf

@ArchangeGabriel
Copy link
Contributor

Now, a small test: using a letter going low for the x label. It just works.
test_x_label.pdf

So it seems that the code is reserving the space for low letters, and we can see that for the Title too were the bbox is including this space even with 0 padding.

@ArchangeGabriel
Copy link
Contributor

Now, some more considerations. When adding plt.rcParams['text.usetex'] = True at the beginning, things get worse:
usetex_tight_default.pdf
usetex_tight_zero.pdf

@jklymak
Copy link
Member

jklymak commented Aug 2, 2018

bounding boxes are not determined optically - they are determined by what the renderer can determine from the properties of the plot. In the case of usetex=False the bounding box has to assume that there might be a descender in the label. There could be a bunch of logic to check the contents of the string, but someone would have to go through and do that carefully. For usetex=True that is even harder because the gif has to be rendered.

I'm not clear what the use-case is that would justify the extra effort to make this work flawlessly. There are image post processing tools that will crop out the whitespace, which seems a lot easier than expecting the composer to do it: http://www.imagemagick.org/script/command-line-options.php#trim

@ArchangeGabriel
Copy link
Contributor

@jklymak The use case is having the desired figure width exactly with no amount of white space whatsoever. Cropping is not an option. I was going to say that saving with bbox_inches='tight',pad_inches=0 reduces the white space in the case of usetex=True but that this is not a desirable thing (remember my last opened issue here?).

@ArchangeGabriel
Copy link
Contributor

tight_1.pdf

For instance here I get way too much white space on the left.

@ArchangeGabriel
Copy link
Contributor

constrained_1.pdf

Constrained does a little better though, so there is still that. Which means you probably did a good job in implementing it @jklymak. ;)

@ArchangeGabriel
Copy link
Contributor

usetex_constrained_zero.pdf

Actually it even does way better when just using the default settings outside usetex=True. ;) So I guess custom fonts are part of the issue here, which makes sense if the rendering is not taken into account.

@ArchangeGabriel
Copy link
Contributor

On a small side note, it seems that the right border is partly cropped in all of my plots. Is that a known tight/constrained layout bug and else should I open one?

@jklymak
Copy link
Member

jklymak commented Aug 2, 2018

By "use case" I mean an instance where this matters to the final presentation. Most plots get put in a paper or on a website, and will have whitespace naturally around the figure as part of the publishing process. Having that being 2 or 3pts off would be pretty hard to determine by eye.

Note that there are also pdf cropping tools: http://pdfcrop.sourceforge.net Again this is an easier post-rendering problem, than trying to do this before rendering.

OTOH, if someone wanted to take the care to make this all be exact pre-rendering, if that is even possible, then I'm sure a PR would be gratefully accepted.

@ArchangeGabriel
Copy link
Contributor

Again, cropping in post-processing is not an option, because it will change the width of the figure (which is fixed to fit exactly in my LaTeX file). I would just use bbox_inches if I did not care about that. ;)

I understand that for most people the difference does not matter, but it does for me. It’s kind of an OCD I have when it comes to layout and typography. I’m not really comfortable with you (nothing personal though, that the case with everyone) using ' for ’ and " for “ and ” for instance. Neither am I with some additional white space around my figure. ;)

Unfortunately I don’t have enough knowledge (or time to acquire it) to contribute code for this, maybe in some distant future… And I understand no-one will want to spend time on this since this seems to require a lot of investment for something that don’t really matter in most cases. But at least now I know the quirks and will be able to get as close as I can. ;)

In the meantime, if you could just have a look and answer my previous comment, that would be amazing. :)

@ArchangeGabriel
Copy link
Contributor

And actually, even with custom fonts your constrained_layout seems to be doing things really well.
custom_constrained_default.pdf

I’m digging a bit more around this, but there seems to be only one case where I get white space on the left (I’m OK ignoring the bottom one, I actually think it’s more consistent to always assume a descender so that two figures side by side would be identical). I’ll report when I will have been able to pinpoint the problematic case.

And then there is just the small cropping on the right side remaining.

@ArchangeGabriel
Copy link
Contributor

OK, found it. I think it is a bug (or maybe not, depending on the desired behaviour) that is not pertaining to this discussion but more general:
normal.pdf
no_descender.pdf

So if there is no descender, the y-label is “right” (or bottom, it’s a matter of POV) aligned in comparison to the other case. Note that the bbox remains exactly the same in both cases.

I think we would want the baseline (where letters like “a” seat) of text to always have the same distance to the figure, don’t we? If so, should I open a new bug report for this?

If that and the small cropping on the right are fixed, then this whole issue can be considered as fixed for me thanks to constrained_layout.

@jklymak
Copy link
Member

jklymak commented Aug 2, 2018

I have a PR in for aligning the labels with respect to the axes that changes the default to aligning the baseline of the text.

I think the correct thing to do is for the size of the axes to be the same regardless of whether a particular label has a descender or not.

@jklymak
Copy link
Member

jklymak commented Aug 2, 2018

I think #11499

@jklymak
Copy link
Member

jklymak commented Aug 2, 2018

With respect to the cropping on the right hand spine... Its possible this is fixed in master because the bbox of the spine is now included, but I can’t check right now. If not the issue is that the width of the line is not being taken into account. I’d actually be surprised if this was really cropped if you embedded the figure in a surrounding document but the line will of course overspill by half the linewidth.

@ArchangeGabriel
Copy link
Contributor

You work in #11499 likely solves the alignment part. Great work!

Regarding the right hand spine… here is your surprise:
beamer.pdf

Just took my constrained_1.pdf above and compiled that file:

\documentclass{beamer}
\setbeamertemplate{navigation symbols}{}

\begin{document}

\begin{frame}
    \includegraphics{constrained_1.pdf}
\end{frame}

\end{document}

@jklymak
Copy link
Member

jklymak commented Aug 3, 2018

Ok well I guess the clip path is the clip path.

I don’t think it makes too much sense to check the line thickness of the spine when checking it’s bbox, but someone could try and add that if they felt compelled. To my thinking the thickness of that line shouldn’t change the size of the plot. If people want to make sure it’s not clipped they can include a reasonable pad.

@ArchangeGabriel
Copy link
Contributor

Can you point me to where in the code this is checked? Maybe that’s a very small and easy change to be made, so I might have a look.

Else I’ll probably live with a w_pad=0.01 (sufficient enough for avoiding the clipping). As long as I’m sure the same exact minimal amount of white space is added around my figures, that’s probably fine. ;)

Anyway with everything else being sorted, I’m already moving toward my next issue. ;)

@jklymak
Copy link
Member

jklymak commented Aug 3, 2018

I’m not on computer but it’d be in spines.py under get_window_extent I think

@ArchangeGabriel
Copy link
Contributor

ArchangeGabriel commented Aug 4, 2018

I’ve setup a master install, and I can say that:

With respect to the cropping on the right hand spine... Its possible this is fixed in master because the bbox of the spine is now included, but I can’t check right now. If not the issue is that the width of the line is not being taken into account.

is in fact not fixed in master.

I’ve took a look at spines.py and get_window_extent, but as I was afraid of, I lack knowledge of matplotlib code as well as of some python to understand what is going on.

I’ll leave this like that for now and eventually use w_pad=0.01 to circumvent the issue, I have bigger problems with my fig.get_tightbbox(fig.canvas.get_renderer()).get_points(); fig.set_size_inches(…) workflow. ;)

@ArchangeGabriel
Copy link
Contributor

Just as summary for anyone coming here:
– the initially reported issue seems long fixed;
– small additional padding remains when using tight_layout(pad=0) and text.usetex = True, but no issue with constrained_layout;
– both constrained_layout and tight_layout are trimming a bit the right hand spine when this one is on the edge of the figure. This can be worked around by adding the smallest possible padding with tight_layout(pad=0.1) or for constrained layout set_constrained_layout_pads(w_pad=0.01, h_pad=0).

@github-actions
Copy link

github-actions bot commented Apr 5, 2023

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Apr 5, 2023
@tacaswell
Copy link
Member

Given the summary in #7806 (comment) I am going to close this as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: inactive Marked by the “Stale” Github Action
Projects
None yet
Development

No branches or pull requests

5 participants