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
Feature Request: manually set colorbar without mappable #3644
I started implementing this and nearly submitted a PR but then it occurred to me that I'm not sure how this should behave in case there is a mappable in the plot already. For example, what should the following code snippet do?
I started implementing this and nearly submitted a PR but then it occurred
to me that I'm not sure how this should behave in case there is a mappable
in the plot already. For example, what should the following code snippet do?
import matplotlib.pyplot as pltimport matplotlib.cm as cm
im = plt.imshow(np.random.random((40, 40)), cmap=cm.coolwarm)
plt.colorbar(cmap=cm.hot, vmin=1.2, vmax=4.3)
The two options I see are:
(i) Ignore the existing mappable and create a colorbar that is independent
of the data in the plot.
(ii) Change the colormap and limits of im so that they reflect the new
values passed to colorbar.
I'm unsure which one is better. Any opinions?
—
Reply to this email directly or view it on GitHub #3644 (comment)
.
I have not been able to find anything online that clearly explains what the term "mappable" means.
If one creates something like a filled contour plot, the colors on the colorbar must correspond to the colors appearing in the contour plot. But, one might not want these colors to cover the limits of the data.
An important use case is the following: One creates a group of contour plots, and wants to then create a colorbar that is common to all of the plots. It appears that there is currently no good mechanism for doing this.
#!/usr/bin/env python'''Make a set of contour plots with a single colormap and colorbar.'''importmatplotlib.pyplotaspltimportnumpyasnpfrommatplotlib.font_managerimportFontPropertiesfromnumpy.randomimportrandcmap=plt.get_cmap('Blues')
clevs=np.arange(0, 4.001, 0.2)
Nr=3Nc=2fig=plt.figure()
figtitle='Multiple contour plots, same levels't=fig.text(0.5, 0.95, figtitle,
horizontalalignment='center',
fontproperties=FontProperties(size=16))
cax=fig.add_axes([0.2, 0.08, 0.6, 0.04])
w=0.4h=0.22contour_sets= []
foriinrange(Nr):
forjinrange(Nc):
pos= [0.075+j*1.1*w, 0.18+i*1.2*h, w, h]
ax=fig.add_axes(pos)
ifi>0:
ax.set_xticklabels([])
ifj>0:
ax.set_yticklabels([])
ax.locator_params(axis='y', nbins=5)
# Make some fake data with a range that varies# somewhat from one plot to the next.data=i+j+rand(10, 20)
cs=ax.contourf(data, levels=clevs, cmap=cmap)
contour_sets.append(cs)
# The colorbar can be based on any of the mappables (the ContourSet# objects) because they are all using the same levels and cmap.fig.colorbar(contour_sets[0], cax, orientation='horizontal')
plt.show()
Having a common colorbar for multiple axes is part of what I was looking for, but what I'd really like is the ability to explicitly set the limits of the colorbar via arguments to the colorbar method.
Reacted by Eliah Rusin, naught101, Corbin Foucart, Ilya Flyamer and Nachiket Gokhale
Your example based on http://matplotlib.org/examples/pylab_examples/multi_image.html was closer to what I've been looking for. Ideally, I'd like to be able to make a colorbar that shows the mapping between data values and colors appearing in one or more contour plots, but with the limits of the colorbar not tied to the limits of the data.
You can do that with the example I gave. For a filled contour plot, the limits of the colorbar are take from the top and bottom values of the region boundaries, clevs in my example. You can set those to anything you like, regardless of the actual data values being contoured. In particular, you can set them to cover a smaller range, and use the extend='both' kwarg to contourf so that out-of-range values are colored with the "over" and "under" values of the colormap, and the colorbar has triangular ends to show these over and under regions.
I'm inclined to close this; I don't support any of the suggestions made earlier in the discussion. Am I missing something? Is there an important use case that our present code can't handle adequately?
But this isn't intuitive/easy for users -- you more or less need to go to stackoverflow or dig deep into the documentation/examples to figure this out. So the proposal/feature request was to make this a one-liner and have matplotlib execute something like the above code under the hood (without the user needing to know what a ScalarMappable is).
Reacted by Brook, Max Kühn, Zhitao Gong, nogilnick, Weronika Patena, Moritz Münst, Hans Krupakar, JElfner, Matthew Wilson and mzechmeister
While I agree that sm.set_array([]) is pretty annoying in there
I think plt.cm.ScalarMappable is also a bit cumbersome for a new user. The idea of a colormap is very intuitive, especially when you can just pass it as a string, e.g. plt.imshow(data, cmap='viridis'). But a ScalarMappable is a bit mysterious and the docs only seem to give a very brief description.
Couldn't this just be documented in a useful fashion?
they also want the automatic construction of the colorbar axes using space stolen from a primary axes.
@efiring This is precisely why one would use fig.colorbar() and not mpl.colorbar.ColorbarBase(). Because fig.colorbar provides the arguments ax and cax which can be used to have the colorbar either attached to an ax (steeling space as usual) or created inside a cax.
The second case it pretty easily achieved with mpl.colorbar.ColorbarBase() as well, but for the first you would need to define your own gridspec or call the colorbar.make_axes method or use the axes_divider.
@ahwillia I don't know what would be confusing about a ScalarMappable - except maybe the name. If you create a colorbar from an image you supply the image to the colorbar function. If you have no image you need to supply something else, and that is a ScalarMapple, i.e. the image without visual representation on screen. I remember that for me it was much more confusing to see plt.colorbar() actually producing something without any argument in many of the examples and I was then rather relieved reading about it taking an argument, namely the ScalarMappable.
At the end I think this can all be handled nicely in a tutorial about colormaps and colorbars.
At the end I think this can all be handled nicely in a tutorial about colormaps and colorbars.
Would be happy to see this as an addition! I just hope it ends up being easy for someone to find via google. The docs are pretty dense... Thanks for the continued interest in improving this!
@efiring I had no idea that you could give colorbar() an array of Axes. I feel like my knowledge about how to lay things out (e.g. constrained layout, grid spec) and all the things that work with it is out of date.
I think saying "Make a ScalarMappable" is an acceptable solution...provided we properly document that this as a straightforward solution. I myself, despite my experience, had no idea it was so easy to make one. This gap in my knowledge probably relates to the fact that none of our examples or tutorials actually ever show the manual instantiation of a ScalarMappable (they allude to it).
@efiring I think I see what you mean about the norm and cmap. But I think enforcing all that would just make the examples too complicated. I've made many many plots with shared colorbars and managed to keep my colormap and vmax/vmin the same between axes w/o invoking higher level machinery 😉
@jklymak, I'm sure you have, but I strongly suspect that there will be users looking at your examples in that tutorial, using them as a model with too little modification, and getting the wrong result. The key is that vmin and vmax must be explicitly set to the same values in all plots, either via the corresponding kwargs in the function or via vmin and vmax kwargs in a single norm instance used in all of the plots. Otherwise, unless the image data in each panel happens to have the same range, the colorbar will be misleading for all but the plot to which it is directly connected.
Implementing support for multicolor lines (similar to plt.scatter) would make @efiring's suggestion to use LineCollections to circumvent the need for manual_colorbar much easier!
Activity
maxalbert commentedon Dec 5, 2014
I started implementing this and nearly submitted a PR but then it occurred to me that I'm not sure how this should behave in case there is a mappable in the plot already. For example, what should the following code snippet do?
The two options I see are:
(i) Ignore the existing mappable and create a colorbar that is independent of the data in the plot.
(ii) Change the colormap and limits of
imso that they reflect the new values passed tocolorbar.I'm unsure which one is better. Any opinions?
WeatherGod commentedon Dec 5, 2014
Even better, what should happen if there is already a colorbar?
On Fri, Dec 5, 2014 at 12:44 PM, maxalbert notifications@github.com wrote:
Phillip-M-Feldman commentedon Jan 16, 2016
I have not been able to find anything online that clearly explains what the term "mappable" means.
If one creates something like a filled contour plot, the colors on the colorbar must correspond to the colors appearing in the contour plot. But, one might not want these colors to cover the limits of the data.
An important use case is the following: One creates a group of contour plots, and wants to then create a colorbar that is common to all of the plots. It appears that there is currently no good mechanism for doing this.
Phillip M. Feldman
efiring commentedon Jan 16, 2016
Here is a modification of http://matplotlib.org/examples/pylab_examples/multi_image.html. Does this do what you want?
Phillip-M-Feldman commentedon Jan 16, 2016
Having a common colorbar for multiple axes is part of what I was looking for, but what I'd really like is the ability to explicitly set the limits of the colorbar via arguments to the colorbar method.
Phillip-M-Feldman commentedon Jan 16, 2016
I've been trying to find something that I can read that explains the concept of 'mappables'. Any pointers will be appreciated.
efiring commentedon Jan 16, 2016
Example of making a colorbar that is not tied to anything else:
http://matplotlib.org/examples/api/colorbar_only.html
"Mappable" refers to an Artist subclass that inherits from the ScalarMappable mixin:
http://matplotlib.org/api/cm_api.html
Phillip-M-Feldman commentedon Jan 17, 2016
Your example based on http://matplotlib.org/examples/pylab_examples/multi_image.html was closer to what I've been looking for. Ideally, I'd like to be able to make a colorbar that shows the mapping between data values and colors appearing in one or more contour plots, but with the limits of the colorbar not tied to the limits of the data.
efiring commentedon Jan 17, 2016
You can do that with the example I gave. For a filled contour plot, the limits of the colorbar are take from the top and bottom values of the region boundaries,
clevsin my example. You can set those to anything you like, regardless of the actual data values being contoured. In particular, you can set them to cover a smaller range, and use theextend='both'kwarg tocontourfso that out-of-range values are colored with the "over" and "under" values of the colormap, and the colorbar has triangular ends to show these over and under regions.Phillip-M-Feldman commentedon Jan 17, 2016
This sounds perfect. I will experiment with this approach. Thanks!
efiring commentedon Jan 17, 2016
I'm inclined to close this; I don't support any of the suggestions made earlier in the discussion. Am I missing something? Is there an important use case that our present code can't handle adequately?
ahwillia commentedon Jan 17, 2016
Close if you want to -- but just to clarify:
The point of the original issue was to request that the interface be simplified. Yes, there are ways to create a colorbar on e.g. line plots:
But this isn't intuitive/easy for users -- you more or less need to go to stackoverflow or dig deep into the documentation/examples to figure this out. So the proposal/feature request was to make this a one-liner and have matplotlib execute something like the above code under the hood (without the user needing to know what a
ScalarMappableis).37 remaining items
ahwillia commentedon Mar 20, 2018
I think
plt.cm.ScalarMappableis also a bit cumbersome for a new user. The idea of a colormap is very intuitive, especially when you can just pass it as a string, e.g.plt.imshow(data, cmap='viridis'). But aScalarMappableis a bit mysterious and the docs only seem to give a very brief description.I'd be happy with that as well.
efiring commentedon Mar 20, 2018
@ImportanceOfBeingErnest That's not exactly what they want; they also want the automatic construction of the colorbar axes using space stolen from a primary axes. I don't think your version would run--it doesn't provide an axes or a way of making one.
@dopplershift, see https://matplotlib.org/gallery/images_contours_and_fields/multi_image.html#sphx-glr-gallery-images-contours-and-fields-multi-image-py.
Also see https://matplotlib.org/tutorials/intermediate/constrainedlayout_guide.html#sphx-glr-tutorials-intermediate-constrainedlayout-guide-py, but those examples need to be modified to be correct--they need to specify the same norm and cmap for each image, as in the multi-image gallery example. As they stand, they will lead people astray. Attn @jklymak...
ImportanceOfBeingErnest commentedon Mar 20, 2018
@efiring This is precisely why one would use
fig.colorbar()and notmpl.colorbar.ColorbarBase(). Becausefig.colorbarprovides the argumentsaxandcaxwhich can be used to have the colorbar either attached to anax(steeling space as usual) or created inside acax.or
The second case it pretty easily achieved with
mpl.colorbar.ColorbarBase()as well, but for the first you would need to define your own gridspec or call the colorbar.make_axes method or use the axes_divider.@ahwillia I don't know what would be confusing about a ScalarMappable - except maybe the name. If you create a colorbar from an image you supply the image to the colorbar function. If you have no image you need to supply something else, and that is a ScalarMapple, i.e. the image without visual representation on screen. I remember that for me it was much more confusing to see
plt.colorbar()actually producing something without any argument in many of the examples and I was then rather relieved reading about it taking an argument, namely the ScalarMappable.At the end I think this can all be handled nicely in a tutorial about colormaps and colorbars.
ahwillia commentedon Mar 20, 2018
Yes, this is what makes it confusing.
Would be happy to see this as an addition! I just hope it ends up being easy for someone to find via google. The docs are pretty dense... Thanks for the continued interest in improving this!
dopplershift commentedon Mar 20, 2018
@efiring I had no idea that you could give
colorbar()an array ofAxes. I feel like my knowledge about how to lay things out (e.g. constrained layout, grid spec) and all the things that work with it is out of date.I think saying "Make a ScalarMappable" is an acceptable solution...provided we properly document that this as a straightforward solution. I myself, despite my experience, had no idea it was so easy to make one. This gap in my knowledge probably relates to the fact that none of our examples or tutorials actually ever show the manual instantiation of a
ScalarMappable(they allude to it).jklymak commentedon Mar 20, 2018
@efiring I think I see what you mean about the norm and cmap. But I think enforcing all that would just make the examples too complicated. I've made many many plots with shared colorbars and managed to keep my colormap and vmax/vmin the same between axes w/o invoking higher level machinery 😉
efiring commentedon Mar 20, 2018
@jklymak, I'm sure you have, but I strongly suspect that there will be users looking at your examples in that tutorial, using them as a model with too little modification, and getting the wrong result. The key is that vmin and vmax must be explicitly set to the same values in all plots, either via the corresponding kwargs in the function or via vmin and vmax kwargs in a single norm instance used in all of the plots. Otherwise, unless the image data in each panel happens to have the same range, the colorbar will be misleading for all but the plot to which it is directly connected.
ImportanceOfBeingErnest commentedon Mar 20, 2018
Would someone be able to comment on why exactly a
ScalarMappableis initialized withself._A = None?matplotlib/lib/matplotlib/cm.py
Line 199 in 1d73e73
What would be the implication if that is changed to
self._A = []?jklymak commentedon Mar 21, 2018
Do it and run the tests!
ahwillia commentedon Apr 25, 2018
Just want to add a cross-reference to: #6040
Implementing support for multicolor lines (similar to
plt.scatter) would make @efiring's suggestion to use LineCollections to circumvent the need formanual_colorbarmuch easier!develop a plot_poles_colorbar function that can plot multiple poles u…
develop a plot_poles_colorbar function that can plot multiple poles u…