You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Colorbar cannot be added to another figure #25871
Calling the colorbar method of one Figure object does not attach the colour bar to that figure, but to the one where the object informing the colour scale resides.
oh, that should definitly raise! On consideration this should warn.
I suspect that that is going on here is because you did not specify an Axes to put the color bar in it is asking the AxesImage object about it's containing Axes and then stealing space from that Axes to put in the color bar. It then (surprisingly!) does the right thing and steals the space from the Axes containing the image in the other figure and then continues to do the right thing and puts the colorbar in that Axes!
I think the correct path here is to, if we have been asked to steal space, verify that the axes we are going to steal from is in the (Sub)Figure we are calling it from.
If we want to flatly error of the image is not an (eventual) child of the figure I think we will have to go through a warning cycle as I have a strong suspicion that users have found they can use this to put colorbars in a different figure for "reasons". On one hand, I do not think this is something we meant to support and conceptually is a bit fishy to me, but on the other hand we should be cautious about breaking users for "that works and we do not think it should" reasons.
Labeling as good first issue because I think than when stealing space that this "works" is a bug (which is clear) so the work is
add a check before we steal space
add a test that it fails
however medium difficulty as I think that a more through analysis of my comments above for "should we always raise?!" needs to be done and a plan for deprecation of that needs to be made.
I have a strong suspicion that users have found they can use this to put colorbars in a different figure for "reasons".
How so? Right now they cannot. This actually what caused me to write this report.
As for “doing the right thing”: If I wanted Matplotlib to guess what I want, I would use that legacy interface. Instead I want Matplotlib to do what it’s told (or at least tell me that it refuses).
It is doing exactly what you told it to. If you do not pass cax or ax then Figure.colorbar will steal space from the Axes that the scalar mappable is in.
Sorry to slightly hijack the thread here, but I think this may be a good opportunity to discuss whether colorbar() could rather be a method on the ScalarMappable rather than on the Figure (we can always keep figure.colorbar(...) available "forever", of course, for backcompat).
Indeed, in an call like fig.colorbar(mappable) or fig.colorbar(mappable, ax=...) or fig.colorbar(mappable, cax=...), fig plays no role at all (*): the axes where the colorbar ends up is entirely determined by the mappable's axes (or by the ax kwarg, or the cax kwarg.
(*) Actually I lied a bit: we currently check whether there's a layout engine on fig (but that actually seems more like a bug; we should really check that on mappable.axes.figure).
It is doing exactly what you told it to. If you do not pass cax or ax then Figure.colorbar will steal space from the Axes that the scalar mappable is in.
The problem here is that colorbar being a method of Figure strongly suggests that you are adding your colour bar to the respective figure. As that would be a reasonable behaviour, I had seen to reason to question it and look into the documentation. In general, that’s fine because a good interface is as self-explanatory as possible. So, the documentation was not the problem (though the improvements were fine), the object structure was. Instead of colorbar being a method of Figure, there should be something like:
axes.make_colorbar(image) instead of fig.colorbar(image,cax=axes)
axes.add_colorbar(image) instead of fig.colorbar(image,ax=axes)
Let's perhaps try to keep the discussion on API design at #25880, but to answer here quickly: I agree that due to the "steal from multiple axes case", having an axes method doesn't really work, but a method on mappables works just fine (image.colorbar(ax=axs)).
is this issue still open and it would seem to me that now the issue may be a bit different can I get some clarification into what needs to be done? this would be my first issue here so id like to properly understand the issue.
It's easy to solve this problem, you can get the colorbar attached to both of the two figures.
That’s just two images with their own colour bars. None of the images has the colour bar belonging to the other. Also, this issue is not about solving a particular problem, but the interface not acting as expected.
I think we should warn if what ends up being the cax is in a different figure than the figure it is called from (with maybe some forgiveness for SubFigure sharing).
I do not think there is a bug, just a surprising behavior due to way the API evolved.
I think that this is still a live issue and the work is:
implement a way to detect of the mappable is in a different Figure than the one fig.colorbar is being called on (we should forgive mappables with no host Figure).
We do not formally assign issues. You can leave a note that you are working on this to make people aware this is looked into (which you’ve done with your comment 😃 👍 ). Please open a pull request if you have a solution.
ok thanks for guidance, one more help I want that
I am not getting file address of this bug, In which file/folder need to make commit please help me with that
I agree With @rcomer , this was addressed by #27458 but was not properly xrefed, closing as done.
@MohitHire Sorry for the confusion, please have a look at another issue. In general, at least half of the work of fixing a bug is sorting out where to fix the bug so it is not always trivial to predict which file to start editing.
Activity
tacaswell commentedon May 12, 2023
oh, that should definitly raise!On consideration this should warn.I suspect that that is going on here is because you did not specify an
Axesto put the color bar in it is asking theAxesImageobject about it's containingAxesand then stealing space from thatAxesto put in the color bar. It then (surprisingly!) does the right thing and steals the space from the Axes containing the image in the other figure and then continues to do the right thing and puts the colorbar in that Axes!I think the correct path here is to, if we have been asked to steal space, verify that the axes we are going to steal from is in the
(Sub)Figurewe are calling it from.If we want to flatly error of the image is not an (eventual) child of the figure I think we will have to go through a warning cycle as I have a strong suspicion that users have found they can use this to put colorbars in a different figure for "reasons". On one hand, I do not think this is something we meant to support and conceptually is a bit fishy to me, but on the other hand we should be cautious about breaking users for "that works and we do not think it should" reasons.
Labeling as good first issue because I think than when stealing space that this "works" is a bug (which is clear) so the work is
however medium difficulty as I think that a more through analysis of my comments above for "should we always raise?!" needs to be done and a plan for deprecation of that needs to be made.
tacaswell commentedon May 12, 2023
Also, if you change the images to have different color maps:
it is clear that it is "doing the right thing", just a very surprising thing!
Wrzlprmft commentedon May 12, 2023
How so? Right now they cannot. This actually what caused me to write this report.
As for “doing the right thing”: If I wanted Matplotlib to guess what I want, I would use that legacy interface. Instead I want Matplotlib to do what it’s told (or at least tell me that it refuses).
tacaswell commentedon May 12, 2023
It is doing exactly what you told it to. If you do not pass
caxoraxthenFigure.colorbarwill steal space from theAxesthat the scalar mappable is in.If you do
you get


and if you do
you get
The docstrings on
colorbaris not the most clear: https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.colorbar but foraxit says these are the axes that will have space stolen from them ifcaxis not set (and it is missing that ifaxis not passed theaxesof themappablewill be used).anntzer commentedon May 12, 2023
Sorry to slightly hijack the thread here, but I think this may be a good opportunity to discuss whether colorbar() could rather be a method on the ScalarMappable rather than on the Figure (we can always keep figure.colorbar(...) available "forever", of course, for backcompat).
Indeed, in an call like
fig.colorbar(mappable)orfig.colorbar(mappable, ax=...)orfig.colorbar(mappable, cax=...), fig plays no role at all (*): the axes where the colorbar ends up is entirely determined by the mappable's axes (or by the ax kwarg, or the cax kwarg.(*) Actually I lied a bit: we currently check whether there's a layout engine on fig (but that actually seems more like a bug; we should really check that on mappable.axes.figure).
Wrzlprmft commentedon May 13, 2023
The problem here is that
colorbarbeing a method ofFigurestrongly suggests that you are adding your colour bar to the respective figure. As that would be a reasonable behaviour, I had seen to reason to question it and look into the documentation. In general, that’s fine because a good interface is as self-explanatory as possible. So, the documentation was not the problem (though the improvements were fine), the object structure was. Instead ofcolorbarbeing a method ofFigure, there should be something like:axes.make_colorbar(image)instead offig.colorbar(image,cax=axes)axes.add_colorbar(image)instead offig.colorbar(image,ax=axes)… or like @anntzer suggested:
image.colorbar()instead offig.colorbar(image)image.colorbar(use_axes=axes)instead offig.colorbar(image,cax=axes)image.colorbar(add_to=axes)instead offig.colorbar(image,ax=axes)jklymak commentedon May 13, 2023
Matplotlib explicitly allows stealing space from multiple axes:
which if you didn't have this, you get the colorbar attached to one axes that is then forced to be smaller than the rest.
I wouldn't object to
mappable.colorbaroraxes.colorbaras shortcuts tofigure.colorbar, but the basic API should be via (sub)figure.anntzer commentedon May 13, 2023
Let's perhaps try to keep the discussion on API design at #25880, but to answer here quickly: I agree that due to the "steal from multiple axes case", having an axes method doesn't really work, but a method on mappables works just fine (
image.colorbar(ax=axs)).klorine28 commentedon Jun 29, 2023
is this issue still open and it would seem to me that now the issue may be a bit different can I get some clarification into what needs to be done? this would be my first issue here so id like to properly understand the issue.
Sc1xya commentedon Jun 30, 2023
It's easy to solve this problem, you can get the colorbar attached to both of the two figures.
Wrzlprmft commentedon Jun 30, 2023
That’s just two images with their own colour bars. None of the images has the colour bar belonging to the other. Also, this issue is not about solving a particular problem, but the interface not acting as expected.
jklymak commentedon Jun 30, 2023
Is there a practical issue here?
tacaswell commentedon Jun 30, 2023
I think we should warn if what ends up being the
caxis in a different figure than the figure it is called from (with maybe some forgiveness for SubFigure sharing).I do not think there is a bug, just a surprising behavior due to way the API evolved.
tacaswell commentedon Sep 18, 2023
I think that this is still a live issue and the work is:
Figurethan the onefig.colorbaris being called on (we should forgive mappables with no hostFigure).Mihirshrivas commentedon Oct 3, 2023
The colorbar attached to both figure , by the following code 👍
from matplotlib.pyplot import subplots, show
import numpy as np
fig1, axes1 = subplots()
fig2, axes2 = subplots()
fig1.suptitle("Figure 1")
fig2.suptitle("Figure 2")
image1 = axes1.imshow(np.random.random((9,9)))
image2 = axes2.imshow(np.random.random((9,9)))
fig2.colorbar(image1)
fig1.colorbar(image2)
show()
timhoffm commentedon Mar 5, 2024
We do not formally assign issues. You can leave a note that you are working on this to make people aware this is looked into (which you’ve done with your comment 😃 👍 ). Please open a pull request if you have a solution.
MohitHire commentedon Mar 5, 2024
ok thanks for guidance, one more help I want that
I am not getting file address of this bug, In which file/folder need to make commit please help me with that
rcomer commentedon Mar 5, 2024
Actually it looks like the task outlined at #25871 (comment) was completed at #27458. Is there anything more to do here?
MohitHire commentedon Mar 5, 2024
I am not able getting in which file should I make Pull request for this issue
tacaswell commentedon Mar 5, 2024
I agree With @rcomer , this was addressed by #27458 but was not properly xrefed, closing as done.
@MohitHire Sorry for the confusion, please have a look at another issue. In general, at least half of the work of fixing a bug is sorting out where to fix the bug so it is not always trivial to predict which file to start editing.