Skip to content

Feature Request: manually set colorbar without mappable #3644

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
ahwillia opened this issue Oct 14, 2014 · 59 comments · Fixed by #13171
Closed

Feature Request: manually set colorbar without mappable #3644

ahwillia opened this issue Oct 14, 2014 · 59 comments · Fixed by #13171

Comments

@ahwillia
Copy link

There are some hacks for doing this: http://stackoverflow.com/questions/8342549/matplotlib-add-colorbar-to-a-sequence-of-line-plots

But it would be nice if this was just a single line of code:

plt.colorbar(cmap=cm.jet,min=0.0,max=1.0)
@tacaswell tacaswell added this to the unassigned milestone Oct 14, 2014
@maxalbert
Copy link
Contributor

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 plt
import 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?

@WeatherGod
Copy link
Member

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:

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)
.

@Phillip-M-Feldman
Copy link

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
Copy link
Member

efiring commented Jan 16, 2016

Here is a modification of http://matplotlib.org/examples/pylab_examples/multi_image.html. Does this do what you want?

#!/usr/bin/env python
'''
Make a set of contour plots with a single colormap and colorbar.
'''

import matplotlib.pyplot as plt
import numpy as np

from matplotlib.font_manager import FontProperties
from numpy.random import rand

cmap = plt.get_cmap('Blues')
clevs = np.arange(0, 4.001, 0.2)

Nr = 3
Nc = 2

fig = 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.4
h = 0.22

contour_sets = []
for i in range(Nr):
    for j in range(Nc):
        pos = [0.075 + j*1.1*w, 0.18 + i*1.2*h, w, h]
        ax = fig.add_axes(pos)
        if i > 0:
            ax.set_xticklabels([])
        if j > 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()

@Phillip-M-Feldman
Copy link

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
Copy link

I've been trying to find something that I can read that explains the concept of 'mappables'. Any pointers will be appreciated.

@efiring
Copy link
Member

efiring commented 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
Copy link

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
Copy link
Member

efiring commented 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, 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.

@Phillip-M-Feldman
Copy link

This sounds perfect. I will experiment with this approach. Thanks!

@efiring
Copy link
Member

efiring commented 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
Copy link
Author

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:

sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
sm._A = []
plt.colorbar(sm)

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).

@efiring
Copy link
Member

efiring commented Jan 17, 2016

What is the use case for a colorbar with a line plot? Is it something other than what one would do with a LineCollection, e.g.,
http://matplotlib.org/examples/pylab_examples/line_collection2.html
or
http://matplotlib.org/examples/pylab_examples/multicolored_line.html ?
How would the colorbar be connected to other plot elements in your use case?

@ahwillia
Copy link
Author

Well my use case was an animation where the lines were changing color over time. For example, representing flow in a network, or electricity.

I wanted a colorbar on the side to show the range of the colormap I was using. So, for example, if the maximum flow allowable was 1.0, it would be nice to just specify:

plt.colorbar(cmap=cm.jet,min=0.0,max=1.0)

Edit: Also, while the LineCollection example is a nice one, it isn't immediately obvious to the common user that it exists and how to take advantage of it. Whereas I think the above one-liner is likely something people would try off the bat (especially if the min/max options are listed in the documentation for colorbar).

@Phillip-M-Feldman
Copy link

Phillip-M-Feldman commented Jan 18, 2016

My use case is in some respects similar. I want to be able to produce a
sequence of filled contour plots with a colorbar that remains invariant
regardless of what the data is doing. The attached script, which I created
with the assistance of Jim Corson at Enthought, demonstrates the creation
of a single filled contour plot with an overridden colorbar. I have two
concerns: (1) It seems that the amount of code that is required to
accomplish this relatively simple job is excessive. (2) What's going on is
rather non-intuitive.

Phillip

On Sun, Jan 17, 2016 at 1:25 PM, Alex Williams notifications@github.com
wrote:

Well my use case was an animation where the lines were changing color over
time. For example, representing flow in a network, or electricity.

I wanted a colorbar on the side to show the range of the colormap I was
using. So, for example, if the maximum flow allowable was 1.0, it would
be nice to just specify:

plt.colorbar(cmap=cm.jet,min=0.0,max=1.0)


Reply to this email directly or view it on GitHub
#3644 (comment)
.

"""
contour_demo.py

OVERVIEW

matplotlib currently provides no good mechanism for creating a filled contour
plot with an accompanying colorbar whose limits are set independently of the
limits of the data.  The need for this functionality is particularly important
when one creates either a sequence of plots or an array of subplots with a
common colorbar.  It is hoped that matplotlib feature request #3644, 'manually
set colorbar without mappable', which was opened Oct 13, 2014, will eventually
lead to a clean solution.

In the meantime, this Python script demonstrates an ugly but effective solution
to the problem.  The script was created with the assistance of Jim Corson at
Enthought.

AUTHOR

Phillip M. Feldman
"""

from numpy import linspace, mgrid, sqrt
from matplotlib import colors, pyplot

# In following statement, we change the background color (`facecolor`) of the

# margin area of the figure from the default dark gray to white.  (Black text on

# a dark gray background gives poor contrast).

fig= pyplot.figure(figsize=(9, 7), facecolor=[1, 1, 1])

# The purpose of the following block of code is to create a "mappable object"

# and the associated colorbar range.  `N_bands` is the number of color bands

# desired.

cmin, cmax= (0, 4)
N_bands= 12
Z= [[0,0],[0,0]]

# `numpy.linspace` produces an array of `num` uniformly-spaced values.  Because

# linspace by default produces a sequence that includes both the initial

# (`cmin`) and final (`cmax`) points, the number the of values must be one

# greater than the number of bands:

levels= linspace(cmin, cmax, num=N_bands+1)
cbar_range= pyplot.contourf(Z, levels)

# The following statement clears the figure, but the mappable object still

# exists:

fig.clf()

# Create a single axes that (except for labels) fills the entire figure space:

axes= fig.add_subplot(111)

x, y= mgrid[0:4:100j, -3:3:200j]
z= sqrt(x**2 + y**2)
xvec= mgrid[-3:3:200j] # 200 values including -3 and 3
yvec= mgrid[0:4:100j] # 100 values including 0 and 2

axes.contourf(xvec, yvec, z, N_bands)
axes.set_xlabel('x', fontsize=14)
axes.set_ylabel('y', fontsize=14)

axes.grid()

cbar= fig.colorbar(cbar_range)
title= axes.set_title("matplotlib feature request #3644\n"
  "(manually set colorbar)", fontsize= 16, y=1.02)

cbar.set_label('power')
cbar.set_ticks([0, 1, 2, 3, 4])
pyplot.show()
fig.savefig("contour_demo.png")

@efiring
Copy link
Member

efiring commented Jan 18, 2016

On 2016/01/17 6:13 PM, Phillip M. Feldman wrote:

I have two
concerns: (1) It seems that the amount of code that is required to
accomplish this relatively simple job is excessive. (2) What's going on is
rather non-intuitive.

It's downright bizarre. Either it is trying to do something I still
don't understand, or it results from a misunderstanding of how contourf
and colorbar work together.

@efiring
Copy link
Member

efiring commented Jan 18, 2016

On 2016/01/17 11:25 AM, Alex Williams wrote:

Well my use case was an animation where the lines were changing color
over time. For example, representing flow in a network, or electricity.

I wanted a colorbar on the side to show the range of the colormap I was
using. So, for example, if the maximum flow allowable was |1.0|, it
would be nice to just specify:

plt.colorbar(cmap=cm.jet,min=0.0,max=1.0)

OK, I can see how an animation of a line with changing color makes the
need for a mappable inconvenient. This is a rare case, but easily
handled with two lines. You can make your own function using calls to
colorbar.make_axes() or colorbar.make_axes_gridspec() to make the
axes in which the colorbar will reside (stealing from the main plot
axes, just like colorbar() does) and then pass that to
colorbar.ColorbarBase.

@tacaswell
Copy link
Member

copy-paste of the code to get highlighting (because us youngins can not deal otherwise).

"""
contour_demo.py


OVERVIEW

matplotlib currently provides no good mechanism for creating a filled contour
plot with an accompanying colorbar whose limits are set independently of the
limits of the data.  The need for this functionality is particularly important
when one creates either a sequence of plots or an array of subplots with a
common colorbar.  It is hoped that matplotlib feature request #3644, 'manually
set colorbar without mappable', which was opened Oct 13, 2014, will eventually
lead to a clean solution.

In the meantime, this Python script demonstrates an ugly but effective solution
to the problem.  The script was created with the assistance of Jim Corson at
Enthought.


AUTHOR

Phillip M. Feldman
"""

from numpy import linspace, mgrid, sqrt
from matplotlib import colors, pyplot

# In following statement, we change the background color (`facecolor`) of the
# margin area of the figure from the default dark gray to white.  (Black text on
# a dark gray background gives poor contrast).
fig= pyplot.figure(figsize=(9, 7), facecolor=[1, 1, 1])

# The purpose of the following block of code is to create a "mappable object"
# and the associated colorbar range.  `N_bands` is the number of color bands
# desired.
cmin, cmax= (0, 4)
N_bands= 12
Z= [[0,0],[0,0]]

# `numpy.linspace` produces an array of `num` uniformly-spaced values.  Because
# linspace by default produces a sequence that includes both the initial
# (`cmin`) and final (`cmax`) points, the number the of values must be one
# greater than the number of bands:
levels= linspace(cmin, cmax, num=N_bands+1)
cbar_range= pyplot.contourf(Z, levels)

# The following statement clears the figure, but the mappable object still
# exists:
fig.clf()

# Create a single axes that (except for labels) fills the entire figure space:
axes= fig.add_subplot(111)

x, y= mgrid[0:4:100j, -3:3:200j]
z= sqrt(x**2 + y**2)
xvec= mgrid[-3:3:200j] # 200 values including -3 and 3
yvec= mgrid[0:4:100j] # 100 values including 0 and 2

axes.contourf(xvec, yvec, z, N_bands)
axes.set_xlabel('x', fontsize=14)
axes.set_ylabel('y', fontsize=14)

axes.grid()

cbar= fig.colorbar(cbar_range)
title= axes.set_title("matplotlib feature request #3644\n"
  "(manually set colorbar)", fontsize= 16, y=1.02)

cbar.set_label('power')
cbar.set_ticks([0, 1, 2, 3, 4])
pyplot.show()
fig.savefig("contour_demo.png")

@Phillip-M-Feldman
Copy link

Hm. You may have a point. I've just realized that z takes values from 0
to 5, which means that this code is producing a colorbar that is
inconsistent with the contour plot. My goal was to create a colorbar that
covers the range from 0 to 4, even though the data extends outside that
interval.

On Sun, Jan 17, 2016 at 10:34 PM, Eric Firing notifications@github.com
wrote:

On 2016/01/17 6:13 PM, Phillip M. Feldman wrote:

I have two
concerns: (1) It seems that the amount of code that is required to
accomplish this relatively simple job is excessive. (2) What's going on
is
rather non-intuitive.

It's downright bizarre. Either it is trying to do something I still
don't understand, or it results from a misunderstanding of how contourf
and colorbar work together.


Reply to this email directly or view it on GitHub
#3644 (comment)
.

@tacaswell
Copy link
Member

I think you want to be doing something like:

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()

x, y = np.mgrid[0:4:100j, -3:3:200j]
z = 2 * np.sqrt(x**2 + y**2)
xvec = np.mgrid[-3:3:200j]  # 200 values including -3 and 3
yvec = np.mgrid[0:4:100j]   # 100 values including 0 and 2

bands = np.linspace(0, 4, 17, endpoint=True)
cf = ax.contourf(xvec, yvec, z, bands, extend='both', cmap='viridis')
ax.set_xlabel('x', fontsize=14)
ax.set_ylabel('y', fontsize=14)

ax.grid()

cbar = fig.colorbar(cf)

cbar.set_label('power')

plt.show()
fig.savefig("contour_demo.png")

so

@Phillip-M-Feldman
Copy link

After replacing extend='both' with extend='neither', I did get exactly
what I was looking for.

In reading the documentation for contour/contourf, I missed the fact that
the behavior is different depending on whether the fourth argument is an
integer or a sequence (list or array). So, if there is a matplotlib issue
here, it involves either (a) the documentation for contour/contourf, or (b)
overuse of positional arguments as opposed to keyword arguments in the
definitions of these functions.

Thanks!

Phillip

On Mon, Jan 18, 2016 at 6:17 AM, Thomas A Caswell notifications@github.com
wrote:

I think you want to be doing something like:

import matplotlib.pyplot as pltimport numpy as np

fig, ax = plt.subplots()

x, y = np.mgrid[0:4:100j, -3:3:200j]
z = 2 * np.sqrt(x2 + y2)
xvec = np.mgrid[-3:3:200j] # 200 values including -3 and 3
yvec = np.mgrid[0:4:100j] # 100 values including 0 and 2

bands = np.linspace(0, 4, 17, endpoint=True)
cf = ax.contourf(xvec, yvec, z, bands, extend='both', cmap='viridis')
ax.set_xlabel('x', fontsize=14)
ax.set_ylabel('y', fontsize=14)

ax.grid()

cbar = fig.colorbar(cf)

cbar.set_label('power')

plt.show()
fig.savefig("contour_demo.png")

[image: so]
https://cloud.githubusercontent.com/assets/199813/12393517/304f4ab8-bdc4-11e5-96f0-9973858aef6a.png


Reply to this email directly or view it on GitHub
#3644 (comment)
.

@naught101
Copy link

See also plt.clim:

Set the color limits of the current image.

@jimmyhmont
Copy link

Is there a way to use contour instead of contourf to create the colorbar this way?

Z= [[0,0],[0,0]]
cbar_range= pyplot.contour(Z, levels)
cbar= fig.colorbar(cbar_range)

I was using it to get a bar with line markers but it doesn't work in matplotlib 2.2.0 anymore.

@afvincent
Copy link
Contributor

@jimmyhmont

import matplotlib.pyplot as plt
plt.ion()
fig, ax = plt.subplots()
Z = [[0, 1],[3, 0]]
cbar_range= ax.contour(Z, 10)
cbar= fig.colorbar(cbar_range, ax=ax)

seems to be working fine for me with Matplotlib 2.2.0 from pip. I guess that the Z array with only null values is somehow causing your issue. IIRC, some dev (maybe @efiring or @jklymak) recently fixed an issue with those kind of all-null input in contour plots.

@story645
Copy link
Member

While this was left in an unresolved state a few years ago, I'd support a PR for this because currently there's no great way to do a colorbar on a collection of filled polygons (like a choropleth)-the way the tutorial recommends (https://matplotlib.org/gallery/api/patch_collection.html#sphx-glr-gallery-api-patch-collection-py) doesn't work that great for geographic polygons so the usual suggested run around is to construct an empty scalermappable to pass into colorbar code. Being able to build it directly would be far more straightforward.

@jklymak
Copy link
Member

jklymak commented Mar 11, 2018

I’d be against adding kwargs to colorbar to do this. vmin and vmax as kwargs that act only on null colorbars would be quite confusing because you know folks would try to use them on normal colorbars as well. On the other hand a new method (nullcorbar ?) makes sense to me.

@naught101
Copy link

What would be to problem with people using them on normal colourbars as well? I think it would be sensible to assume that if vmin/vmax are set, then they should override the detected values.

@efiring
Copy link
Member

efiring commented Mar 19, 2018

@ahwillia A LineCollection is a ScalarMappable (and is also much faster than multiple plot calls), so I recommend that you use it with a normal colorbar call as in this example:
https://matplotlib.org/gallery/lines_bars_and_markers/multicolored_line.html?highlight=linecollection

@ahwillia
Copy link
Author

ahwillia commented Mar 19, 2018 via email

@story645
Copy link
Member

story645 commented Mar 19, 2018

so @phobson led me down to the solution to my problem, which is probably a general solution but also not a thing I love:

world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))

cmap = mpl.cm.viridis_r
norm = mpl.colors.Normalize(vmin=0, vmax=1.25e9)

fig, ax = plt.subplots()
world.plot(column='pop_est', cmap=cmap, norm=norm, ax=ax)
plt.colorbar(ax.collections[0], shrink=.60)
ax.set_aspect('equal')

@ahwillia
Copy link
Author

ahwillia commented Mar 19, 2018 via email

@jklymak
Copy link
Member

jklymak commented Mar 19, 2018

A new feature has to be in demand and well thought out. Then someone has to see it through to a PR. I think a well thought-out PR for this would be very seriously considered.

@efiring
Copy link
Member

efiring commented Mar 19, 2018

@ahwillia We certainly have multiple ways of doing things. The most general example is using pyplot functions versus Axes methods.

The point of the conventional colorbar is to connect the primary plot to the colorbar, so that changing a cmap or norm in the former also changes it in the latter. This is good for maintaining the integrity of the figure, so we want to encourage it whenever possible. And in most cases it is possible.

@dopplershift
Copy link
Contributor

@efiring What about the case of one colorbar for multiple subplots?

@jklymak
Copy link
Member

jklymak commented Mar 19, 2018

... right now one colorbar for multiple subplots is controlled by just one scalar mappable. The "multiple axes" is just a layout consideration. Not sure how else it could work....

@ImportanceOfBeingErnest
Copy link
Member

If I understand correctly what people want as manual_colorbar would be the following 3 line function:

def manual_colorbar(fig, norm, cmap, **kwargs):
    sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
    sm.set_array([])
    return fig.colorbar(sm, **kwargs)

While I agree that sm.set_array([]) is pretty annoying in there (actually it has been introduced with matplotlib 1.2 or 1.3 or so, I remember it suddenly being needed for some unknown reason), it might be questionable if it's worth adding a completely new function as a shortcut.

Couldn't this just be documented in a useful fashion?

@jklymak
Copy link
Member

jklymak commented Mar 20, 2018

Yeah, or added to figure as figure.manual_colorbar. I'd be fine w/ that addition.

@ahwillia
Copy link
Author

ahwillia commented Mar 20, 2018

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?

I'd be happy with that as well.

@efiring
Copy link
Member

efiring commented 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
Copy link
Member

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.

ax = fig.add_subplot(332)
manual_colorbar(fig, norm, cmap, ax=ax)

or

cax = fig.add_axes([....])
manual_colorbar(fig, norm, cmap, cax=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.

@ahwillia
Copy link
Author

except maybe the name

Yes, this is what makes it confusing.

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!

@dopplershift
Copy link
Contributor

@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).

@jklymak
Copy link
Member

jklymak commented 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
Copy link
Member

efiring commented 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
Copy link
Member

Would someone be able to comment on why exactly a ScalarMappable is initialized with self._A = None?

self._A = None

What would be the implication if that is changed to self._A = []?

@jklymak
Copy link
Member

jklymak commented Mar 21, 2018

Do it and run the tests!

@ahwillia
Copy link
Author

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 for manual_colorbar much easier!

Swanson-Hysell added a commit to PmagPy/PmagPy that referenced this issue Sep 26, 2018
…sing a colorbar and develops a colorbar

developing the colorbar was a bit tricky without a mappable object. This matplotlib issue helped: matplotlib/matplotlib#3644
moonshoes87 pushed a commit to PmagPy/PmagPy that referenced this issue Oct 3, 2018
…sing a colorbar and develops a colorbar

developing the colorbar was a bit tricky without a mappable object. This matplotlib issue helped: matplotlib/matplotlib#3644
@QuLogic QuLogic modified the milestones: unassigned, v3.1 Jan 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.