Skip to content

tight_layout reserves tick space even if disabled #11203

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
surchs opened this issue May 9, 2018 · 7 comments · Fixed by #11210
Closed

tight_layout reserves tick space even if disabled #11203

surchs opened this issue May 9, 2018 · 7 comments · Fixed by #11210
Milestone

Comments

@surchs
Copy link

surchs commented May 9, 2018

Bug report

tight_layout seems to reserve space for axes ticks even if they are removed

I like to save a plot with zero surrounding whitespace.

Previously (version 1.5.3) this was possible by setting bbox_inches='tight', pad_inches=0 in the savefig call and removing any axis decorators (ticks, labels). In the current version (version 2.2.2) the whitespace around the plot cannot be fully removed in the above way, even if decorators are removed. It seems that the remaining whitespace corresponds to the area that the axis ticks would be drawn in, had they not been removed.

This seems to be related to recent changes in how axes legends get drawn:

Tools like fig.tight_layout() and fig.savefig(bbox_inches='tight') 
would clip these legends. 
A change was made to include them in the tight calculations.

It seems not possible to save a plot with no surrounding whitespace in the current version.

Code for reproduction

import numpy as np
from matplotlib import pyplot as plt

# axis ticks drawn
f = plt.figure(figsize=(1,1))
ax = f.add_subplot(111)
ax.matshow(np.ones((10,10)), cmap=plt.cm.Blues_r, aspect='auto')
ax.set_xticklabels([])
ax.set_yticklabels([])
#ax.axis('off')
f.savefig('test_ticks.png', dpi=300, bbox_inches='tight', pad_inches=0, facecolor='red')

# axis ticks removed
f = plt.figure(figsize=(1,1))
ax = f.add_subplot(111)
ax.matshow(np.ones((10,10)), cmap=plt.cm.Blues_r, aspect='auto')
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.axis('off')
f.savefig('test_noticks.png', dpi=300, bbox_inches='tight', pad_inches=0, facecolor='red')

Actual outcome (using matplotlib 2.2.2)
test_ticks.png:
image
image dimensions: 239 * 232 pixels

test_noticks.png:
image
image dimensions: 239 * 232 pixels

Expected outcome (using matplotlib 1.5.3)
test_ticks.png:
image
image dimensions: 232 * 232 pixels
(drawing the ticks inside the plot is obviously not desirable as well but I think this illustrates the difference in behaviour)

test_noticks.png:
image
image dimensions: 232 * 232 pixels
(this is what I'd like to get with the above code).

Matplotlib version

  • Operating system: Ubuntu 16.04 LTS
  • Matplotlib version: 2.2.2
  • Matplotlib backend (print(matplotlib.get_backend())): module://ipykernel.pylab.backend_inline (also tried in Qt5Agg)
  • Python version: 3.6.5
  • Jupyter version (if applicable): 4.4.0
  • Other libraries:
backcall==0.1.0
certifi==2018.4.16
cycler==0.10.0
Cython==0.28.2
decorator==4.3.0
ipykernel==4.8.2
ipython==6.3.1
ipython-genutils==0.2.0
jedi==0.12.0
jupyter-client==5.2.3
jupyter-core==4.4.0
kiwisolver==1.0.1
matplotlib==2.2.2
mkl-fft==1.0.0
mkl-random==1.0.1
nibabel==2.2.1
nilearn==0.4.1
numpy==1.14.2
olefile==0.45.1
parso==0.2.0
pexpect==4.5.0
pickleshare==0.7.4
Pillow==5.1.0
prompt-toolkit==1.0.15
ptyprocess==0.5.2
Pygments==2.2.0
pyparsing==2.2.0
PyQt5==5.10.1
python-dateutil==2.7.2
pytz==2018.4
pyzmq==17.0.0
scikit-learn==0.19.1
scipy==1.0.1
simplegeneric==0.8.1
sip==4.19.8
six==1.11.0
tornado==5.0.2
traitlets==4.3.2
wcwidth==0.1.7

All throught conda default channel, the code example was run in two separate virtual environments, one downgraded to matplotlib 1.5.3.

@ImportanceOfBeingErnest
Copy link
Member

Workarounds

To save a plot with no margins around you may leave out the "tight" option and instead make sure the axes actually sits tight against the figure boundaries,

fig, ax = plt.subplots(figsize=(1,1))
fig.subplots_adjust(0,0,1,1)
# plot...
ax.axis('off')
fig.savefig('test.png', dpi=300)

or

fig = plt.figure(figsize=(1,1))
ax = fig.add_axes([0,0,1,1])
# plot...
ax.axis('off')
fig.savefig('test.png', dpi=300)

But it is interesting that the undesired behaviour seems to be due to the ticks. So an option is indeed to let the (turned off) ticks point inwards by adding

plt.rcParams["xtick.direction"] = "in"
plt.rcParams["ytick.direction"] = "in"

This also works fine with the rest of the code from the OP.

Problem

I don't think this has anything to do with the legend being part of the tight layout. Given that the tick direction is responsible for this behaviour I would guess that it has been there all the time, but only shows up now that the ticks point outwards by default. I don't have any 1.x version available right now, but you may try to set

plt.rcParams["xtick.direction"] = "out"
plt.rcParams["ytick.direction"] = "out"

in your 1.5 version and see if that reproduces the undesired behaviour.

@jklymak
Copy link
Member

jklymak commented May 9, 2018

Yeah this isn’t an issue to do w the legend changes. @ImportanceOfBeingErnest is correct that it’s due to the tick direction change in 2.0

@surchs
Copy link
Author

surchs commented May 9, 2018

Thank you very much for your suggestion @ImportanceOfBeingErnest, that does the trick for me!

Re older version: I tried your code to draw the ticks on the outside, that doesn't result in the same behaviour. Instead:
test_ticks.png:
image
test_noticks.png:
image

I think my issue may be related to #10361 which would also overlap with the versions that show and don't show this behaviour.

edit: @jklymak that can't be the only reason as I don't have the same behaviour in 1.5.3 just by drawing the ticks on the outside.

@jklymak
Copy link
Member

jklymak commented May 9, 2018

I'd argue the current behaviour is correct - the tight bounding box should include the ticks. If 1.5.3 didn't do that, I guess there was a behaviour change, but in my opinion it was for the better.

@ImportanceOfBeingErnest
Copy link
Member

Yes the tight box needs to include the ticks for sure. But should it include axes decorators which are not drawn at all; e.g. the ticks when axis("off")?

Pro the current behaviour: You get reproducible plots, independent on whether the axis is turned on or off.
Contra the current behaviour: The result looks unexpected, since pad=0 should mean "no padding".

@jklymak
Copy link
Member

jklymak commented May 9, 2018

Sorry I missed the axis off part. That indeed seems to be a bug

@jklymak
Copy link
Member

jklymak commented May 9, 2018

I think #11210 fixes the problem. I do think it was an unintended bug when outward ticks started being the default, but visibility was overlooked.

@jklymak jklymak added this to the v3.0 milestone May 9, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants