Skip to content

[Bug]: Removing colorbar misaligns other subplots #23502

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

Open
thoinka opened this issue Jul 28, 2022 · 5 comments
Open

[Bug]: Removing colorbar misaligns other subplots #23502

thoinka opened this issue Jul 28, 2022 · 5 comments

Comments

@thoinka
Copy link

thoinka commented Jul 28, 2022

Bug summary

When adding multiple colorbars with plt.colorbar to different subplots and then removing them again, the removal affects the width of the other subplot.

Code for reproduction

fig, ax = plt.subplots(2)
s = ax[0].scatter([0, 1, 2], [0, 1, 2], c=[0, 1, 2])
plt.colorbar(s, ax=ax[0])
cbar = plt.colorbar(s, ax=ax[1])
cbar.remove()

Actual outcome

mpl_bug_actual

Expected outcome

mpl_bug_exp

Additional information

This bug occurs with matplotlib>=3.4.0, matplotlib 3.3.4 produces the picture shown under "expected outcome", which is what I would assume is correct.

Operating system

macOS 11.0.1 and Ubuntu 18.04

Matplotlib Version

matplotlib 3.5.2

Matplotlib Backend

module://ipykernel.pylab.backend_inline

Python version

3.8.5

Jupyter version

4.6.3

Installation

pip

@oscargus
Copy link
Member

Indeed! If anything, the bottom subplot should expand. Definitely not the top one.

@oscargus
Copy link
Member

The problem seems to be that

ax = self.mappable.axes

points at the wrong axis, i.e. the top one. Which leads to that the rest of the code is applied to the top rather than the bottom axis.
try:
gs = ax.get_subplotspec().get_gridspec()
subplotspec = gs.get_topmost_subplotspec()
except AttributeError:
# use_gridspec was False
pos = ax.get_position(original=True)
ax._set_position(pos)
else:
# use_gridspec was True
ax.set_subplotspec(subplotspec)

So self.mappable.axes is not set correctly(?).

@oscargus
Copy link
Member

It is set correctly, it is "just" that the logic doesn't consider this case.

The code assumes that the mappable provided (the scatter plot) is also the object that got the colorbar attached to it and therefore the part that should be updated when the colorbar is removed. Probably one should use ax instead here. (Althiguh that may cause other issues...)

@jklymak
Copy link
Member

jklymak commented Jul 29, 2022

Thanks @oscargus that does indeed seem to be the problem. I'm skeptical we should resize anything, but if that is the existing behaviour we should be storing the result of ax=ax and operating on that (if it is a single axes).

The problem is that the colorbar axes is created outside of colorbar, so the colorbar method never knows if it stole from space anyone or not. I think its overreach for colorbar to then try to give space back...

@jklymak
Copy link
Member

jklymak commented Dec 11, 2022

Just to clarify here:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(2, layout='constrained')
s = ax[0].scatter([0, 1, 2], [0, 1, 2], c=[0, 1, 2])
plt.colorbar(s, ax=ax[0])

plt.show()

Does what I think the OP wants.

If they really don't want to use constrained layout, they can also do various other hacks (inset_axes is the best way), or make a mock scalarmappable

fig, ax = plt.subplots(2)
s = ax[0].scatter([0, 1, 2], [0, 1, 2], c=[0, 1, 2])
plt.colorbar(s, ax=ax[0])
cbar = plt.colorbar(mpl.cm.ScalarMappable(norm=plt.Normalize(), cmap=plt.get_cmap('RdBu_r')),
ax=ax[1])
cbar.remove()

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

3 participants