Skip to content

'bottom cannot be >= top' when using tight_layout #5456

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
blaiseli opened this issue Nov 10, 2015 · 19 comments
Closed

'bottom cannot be >= top' when using tight_layout #5456

blaiseli opened this issue Nov 10, 2015 · 19 comments

Comments

@blaiseli
Copy link

I'm trying to understand why sometimes I get a ValueError when using tight_layout.

The code of matplotlib is too complicated for me, but I'm trying to see what's happening using pdb.

It seems that the problem comes from what get_tight_layout_figure returns:

(Pdb) l
379                                              subplot_list=subplot_list,
380                                              ax_bbox_list=ax_bbox_list,
381                                              pad=pad, h_pad=h_pad, w_pad=w_pad,
382                                              rect=(left, bottom, right, top))
383  
384  ->     return kwargs
(Pdb) p kwargs
{'top': 0.43099934895833336, 'right': 0.97750000000000004, 'bottom': 0.56900065104166675, 'left': 0.16939941406250003}

I guess this in turns comes from what happens in auto_adjust_subplotpars, in the output of _get_bottom and _get_top:

(Pdb) p ax_bbox.ymin
0.3716666666666667
(Pdb) p tight_bbox.ymin
-0.16733398437499999
(Pdb) p ax_bbox.ymin - tight_bbox.ymin
0.53900065104166672
(Pdb) p tight_bbox.ymax
1.1673339843749999
(Pdb) p ax_bbox.ymax
0.6283333333333333
(Pdb) p tight_bbox.ymax - ax_bbox.ymax
0.53900065104166661

I don't know what ax_bbox and tight_bbox exactly are.

I just guess the algorithm to determine the figure properties do not play well with the data I want to plot. Can this be considered a bug ?

@jenshnielsen
Copy link
Member

Could you provide a minimal example of what you are trying to do. That makes it easier to understand if this is a bug and how to fix it.

@tacaswell
Copy link
Member

I have seen exceptions like this before if you have strings (in this case probably ylabels) on a multi-Axes figure where the text is wider than two axes.

Without a minimal example there is not much we can do as, (as you point out) the tight layout code is rather complicated.

@tacaswell tacaswell added this to the unassigned milestone Nov 11, 2015
@tacaswell tacaswell added the status: needs clarification Issues that need more information to resolve. label Nov 11, 2015
@blaiseli
Copy link
Author

I'm currently trying to build a minimal example, but what I can already say is that this indeed has to do with ylabels. If I shorten my label by 4 letters, the bug doesn't show up.

@blaiseli
Copy link
Author

Here's an example:

#!/usr/bin/env python

import matplotlib
matplotlib.use("PDF")
import matplotlib.pyplot as plt

import sys

n = int(sys.argv[1])

x = [1, 2, 3, 4, 5, 6, 7, 8]

xmin = min(x)
xmax = max(x)

data1_f = [9.2579827408192e-05,
           0.0003372918804693761,
           0.0007482795416375103,
           0.0008833191108781534,
           0.0006607025669975578,
           0.00032707300226202557,
           0.00018651452667246514,
           3.514019691439356e-05]
data1_r = [2.605700651541691e-05,
           2.89119480988452e-05,
           4.2170270917124425e-05,
           4.998876011255778e-05,
           3.1114424088781854e-05,
           1.3605595433950519e-05,
           5.257642663359189e-06,
           3.5083320041254324e-06]
data2_f = [8.470149678300792e-05,
           0.0003150887959573901,
           0.0007836372284836411,
           0.0010760646463750388,
           0.0008765372009137937,
           0.00048812392173035456,
           0.00029758922448336404,
           5.93581217984265e-05]
data2_r = [1.816789924161253e-05,
           2.4672634481371817e-05,
           4.221322246098976e-05,
           6.554534103019158e-05,
           4.963479723751039e-05,
           3.1058180581352124e-05,
           1.2640321929190978e-05,
           4.019617548011485e-06]

# I want data1 and data2 on separate graphics, but with the same vertical scale.
ymin = - max(max(data1_r), max(data2_r))
ymax = max(max(data1_f), max(data2_f))

y_span = ymax - ymin
margin = y_span * 0.05

plt.axis([xmin - 0.5, xmax + 0.5, ymin - margin, ymin + margin])
axis = plt.gca()
axis.set_autoscale_on("False")
axis.bar(x, data1_f, facecolor="red", align="center")
axis.bar(x, [-v for v in data1_r], facecolor="blue", align="center")
axis.set_ylabel(" ".join(["data1"] * n))
plt.tight_layout()
plt.savefig("data1.pdf")
plt.cla()

plt.axis([xmin - 0.5, xmax + 0.5, ymin - margin, ymin + margin])
axis = plt.gca()
axis.set_autoscale_on("False")
axis.bar(x, data2_f, facecolor="red", align="center")
axis.bar(x, [-v for v in data2_r], facecolor="blue", align="center")
axis.set_ylabel(" ".join(["data2"] * n))
plt.tight_layout()
plt.savefig("data2.pdf")
plt.cla()

If I call this with n = 15, the bug doesn't occur. With n = 16, it does:

./debug.py 15
/usr/lib/python2.7/dist-packages/matplotlib/tight_layout.py:225: UserWarning: tight_layout : falling back to Agg renderer
  warnings.warn("tight_layout : falling back to Agg renderer")
./debug.py 16
/usr/lib/python2.7/dist-packages/matplotlib/tight_layout.py:225: UserWarning: tight_layout : falling back to Agg renderer
  warnings.warn("tight_layout : falling back to Agg renderer")
Traceback (most recent call last):
  File "./debug.py", line 72, in <module>
    plt.tight_layout()
  File "/usr/lib/python2.7/dist-packages/matplotlib/pyplot.py", line 1288, in tight_layout
    fig.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
  File "/usr/lib/python2.7/dist-packages/matplotlib/figure.py", line 1656, in tight_layout
    self.subplots_adjust(**kwargs)
  File "/usr/lib/python2.7/dist-packages/matplotlib/figure.py", line 1516, in subplots_adjust
    self.subplotpars.update(*args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/matplotlib/figure.py", line 225, in update
    raise ValueError('bottom cannot be >= top')
ValueError: bottom cannot be >= top

@tacaswell tacaswell modified the milestones: next bug fix release (2.0.1), unassigned Nov 18, 2015
@tacaswell tacaswell added status: confirmed bug and removed status: needs clarification Issues that need more information to resolve. labels Nov 18, 2015
@tacaswell
Copy link
Member

fig, ax = plt.subplots()
ax.set_ylabel('a'*50000)
plt.tight_layout()

is a minimal example.

There is a check in tight_layout that the pad can not be negative, but there also needs to be one that it can not be > .5.

josecv pushed a commit to josecv/matplotlib that referenced this issue Mar 15, 2016
josecv pushed a commit to josecv/matplotlib that referenced this issue Mar 15, 2016
@josecv
Copy link

josecv commented Mar 15, 2016

Hey guys we've written up a quick fix for this one and put in a pull request. Let us know if it's cool with you.

Thanks!

josecv pushed a commit to josecv/matplotlib that referenced this issue Mar 15, 2016
josecv pushed a commit to josecv/matplotlib that referenced this issue Mar 16, 2016
CrazyKsv added a commit to CrazyKsv/matplotlib that referenced this issue Mar 16, 2016
josecv pushed a commit to josecv/matplotlib that referenced this issue Mar 18, 2016
@phobson
Copy link
Member

phobson commented Apr 1, 2016

I've got another example that doesn't involve a label that's too long (but maybe too tall?):

from matplotlib import pyplot

fig, ax = pyplot.subplots(figsize=(8, 3))

ax.set_xscale('logit')
ax.set_xlim(left=0.02, right=0.98)
ax.set_xlabel('new logit scale')
ax.set_yticks([])
fig.tight_layout()

Which produces the following (full traceback from jupyter notebook):

C:\Miniconda3\envs\probscale\lib\site-packages\matplotlib\figure.py in tight_layout(self, renderer, pad, h_pad, w_pad, rect)
   1754                                          rect=rect)
   1755 
-> 1756         self.subplots_adjust(**kwargs)
   1757 
   1758 

C:\Miniconda3\envs\probscale\lib\site-packages\matplotlib\figure.py in subplots_adjust(self, *args, **kwargs)
   1610 
   1611         """
-> 1612         self.subplotpars.update(*args, **kwargs)
   1613         for ax in self.axes:
   1614             if not isinstance(ax, SubplotBase):

C:\Miniconda3\envs\probscale\lib\site-packages\matplotlib\figure.py in update(self, left, bottom, right, top, wspace, hspace)
    228             if self.bottom >= self.top:
    229                 reset()
--> 230                 raise ValueError('bottom cannot be >= top')
    231 
    232     def _update_this(self, s, val):

ValueError: bottom cannot be >= top

Dropping into to the debugger, bottom is being set to inf.

@QuLogic QuLogic modified the milestones: 2.0.1 (next bug fix release), 2.0.2 (next bug fix release) May 3, 2017
@tacaswell tacaswell modified the milestones: 2.1.1 (next bug fix release), 2.2 (next feature release) Oct 9, 2017
@madanh
Copy link

madanh commented Jun 5, 2018

Affects me on version 2.2.2

@jklymak
Copy link
Member

jklymak commented Jun 5, 2018

This probably needs a better error message (its possible there already is one for 3.0, I’ve not checked, though I recall this coming up before). However, if you get this error, it means your decorators are not leaving room for your axes, and its size has shrunk to nothing. Either make your decorators smaller or your figure larger.

@timhoffm
Copy link
Member

timhoffm commented Jun 5, 2018

The error message is still the same on master.

@madanh
Copy link

madanh commented Jun 8, 2018

@jklymak

Either make your decorators smaller or your figure larger

It happens when tight_layout() is called. It is resizing the elements that were OK before it was called. So it's a bug there, it must figure automatically if there's no room and leave all as is.

@tacaswell
Copy link
Member

@maahn That should be what it is doing and it is notifying you it can not satisfy the constraints by raising an exception. You can then catch this and do what ever the right thing for your application is.

@jklymak
Copy link
Member

jklymak commented Jun 8, 2018

Right. Or conversely, how small do you want it to make the axes before it errors?

@jklymak
Copy link
Member

jklymak commented Jun 10, 2018

OK, the error here was in the logit scale as in #11386, closed by #11404

@vss888
Copy link

vss888 commented Jun 25, 2018

I got the same error as a result of calling import matplotlib.pyplot as plt; plt.tight_layout(pad=1.0). The workaround, which allowed me to get rid of the error, was to reduce the font size on my legend. So, it seems, one can get the error if the legend does not fit on the figure.

matplotlib.__version__: 2.2.2

@dstansby
Copy link
Member

Using @tacaswell's minimum example now gives

>>> plt.tight_layout()
/Users/dstansby/miniconda3/lib/python3.7/site-packages/matplotlib/tight_layout.py:181: UserWarning: Tight layout not applied. The bottom and top margins cannot be made large enough to accommodate all axes decorations. 
  warnings.warn('Tight layout not applied. '

Which I think is good enough to close this as "fixed with a sensible warning". If I've missed anything, feel free to comment or re-open this issue.

@EliteMasterEric
Copy link

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_ylabel('a'*50000)
plt.tight_layout()

Running the code above still results in the stack trace below, in matplotlib 3.0.3, the latest version available for Python 2.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/dist-packages/matplotlib/pyplot.py", line 1379, in tight_layout
    fig.tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
  File "/usr/lib/python2.7/dist-packages/matplotlib/figure.py", line 1756, in tight_layout
    self.subplots_adjust(**kwargs)
  File "/usr/lib/python2.7/dist-packages/matplotlib/figure.py", line 1612, in subplots_adjust
    self.subplotpars.update(*args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/matplotlib/figure.py", line 230, in update
    raise ValueError('bottom cannot be >= top')
ValueError: bottom cannot be >= top

@jklymak
Copy link
Member

jklymak commented Aug 26, 2019

3.0.3 is not py2 compatible. Maybe you mean 2.2.3? Doubt this fix will be back ported

@EliteMasterEric
Copy link

Apologies, it appears the version of matplotlib I was using as 1.5.1, not 2.2.4.

Upgrading to that version makes the above code function as expected.

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