Set Colorbar Range
Asked Answered
J

7

218

I have the following code:

import matplotlib.pyplot as plt

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}
 
cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)
 
plt.clf()
plt.pcolor(X, Y, v, cmap=cm)
plt.loglog()
plt.xlabel('X Axis')
plt.ylabel('Y Axis')
 
plt.colorbar()
plt.show()

This produces a graph of the values v on the axes X vs Y, using the specified colormap. The X and Y axes are perfect, but the colormap spreads between the min and max of v. I would like to force the colormap to range between 0 and 1.

I thought of using:

plt.axis(...)

To set the ranges of the axes, but this only takes arguments for the min and max of X and Y, not the colormap.

Edit:

For clarity, let's say I have one graph whose values range (0 ... 0.3), and another graph whose values (0.2 ... 0.8).

In both graphs, I will want the range of the colorbar to be (0 ... 1). In both graphs, I want this range of colour to be identical using the full range of cdict above (so 0.25 in both graphs will be the same colour). In the first graph, all colours between 0.3 and 1.0 won't feature in the graph, but will in the colourbar key at the side. In the other, all colours between 0 and 0.2, and between 0.8 and 1 will not feature in the graph, but will in the colourbar at the side.

Jackshaft answered 30/7, 2010 at 16:4 Comment(0)
H
235

Using vmin and vmax forces the range for the colors. Here's an example:

enter image description here

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )

def do_plot(n, f, title):
    #plt.clf()
    plt.subplot(1, 3, n)
    plt.pcolor(X, Y, f(data), cmap=cm, vmin=-4, vmax=4)
    plt.title(title)
    plt.colorbar()

plt.figure()
do_plot(1, lambda x:x, "all")
do_plot(2, lambda x:np.clip(x, -4, 0), "<0")
do_plot(3, lambda x:np.clip(x, 0, 4), ">0")
plt.show()
Hyperesthesia answered 31/7, 2010 at 4:1 Comment(1)
Why is this answer better than the one using plt.clim posted by @Amro ?Boyce
C
133

Use the CLIM function (equivalent to CAXIS function in MATLAB):

plt.pcolor(X, Y, v, cmap=cm)
plt.clim(-4,4)  # identical to caxis([-4,4]) in MATLAB
plt.show()
Cockerel answered 31/7, 2010 at 4:51 Comment(2)
I believe clim() scales the colour axes, but the colours themselves change values. The point at a certain fraction along the scale will be the same colour whatever the scale, but the value it represents will change.Jackshaft
Yes. This is the desired behaviour of the asker, so solves the problem: that the colour scale be identical between graphs.Fujio
L
24

Not sure if this is the most elegant solution (this is what I used), but you could scale your data to the range between 0 to 1 and then modify the colorbar:

import matplotlib as mpl
...
ax, _ = mpl.colorbar.make_axes(plt.gca(), shrink=0.5)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=cm,
                       norm=mpl.colors.Normalize(vmin=-0.5, vmax=1.5))
cbar.set_clim(-2.0, 2.0)

With the two different limits you can control the range and legend of the colorbar. In this example only the range between -0.5 to 1.5 is show in the bar, while the colormap covers -2 to 2 (so this could be your data range, which you record before the scaling).

So instead of scaling the colormap you scale your data and fit the colorbar to that.

Lull answered 30/7, 2010 at 18:3 Comment(2)
I think that's doing something subtly different…sorry I was probably not precise enough in my question. Your solution will scale the colours so that what used to represent the value 1.0 will now represent the max value in my data. The colorbar will show 0..1 as I need it (with vmin=0, vmax=1), but everything above this max value will be the same colour...Jackshaft
... I've updated my question to show what I'm after more clearly. Sorry if I was too vague.Jackshaft
S
18

Using figure environment and .set_clim()

Could be easier and safer this alternative if you have multiple plots:

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )
data1 = np.clip(data,0,6)
data2 = np.clip(data,-6,0)
vmin = np.min(np.array([data,data1,data2]))
vmax = np.max(np.array([data,data1,data2]))

fig = plt.figure()
ax = fig.add_subplot(131)
mesh = ax.pcolormesh(data, cmap = cm)
mesh.set_clim(vmin,vmax)
ax1 = fig.add_subplot(132)
mesh1 = ax1.pcolormesh(data1, cmap = cm)
mesh1.set_clim(vmin,vmax)
ax2 = fig.add_subplot(133)
mesh2 = ax2.pcolormesh(data2, cmap = cm)
mesh2.set_clim(vmin,vmax)
# Visualizing colorbar part -start
fig.colorbar(mesh,ax=ax)
fig.colorbar(mesh1,ax=ax1)
fig.colorbar(mesh2,ax=ax2)
fig.tight_layout()
# Visualizing colorbar part -end

plt.show()

enter image description here

A single colorbar

The best alternative is then to use a single color bar for the entire plot. There are different ways to do that, this tutorial is very useful for understanding the best option. I prefer this solution that you can simply copy and paste instead of the previous visualizing colorbar part of the code.

fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
                    wspace=0.4, hspace=0.1)
cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
cbar = fig.colorbar(mesh, cax=cb_ax)

enter image description here

P.S.

I would suggest using pcolormesh instead of pcolor because it is faster (more infos here ).

Shaynashayne answered 22/8, 2018 at 16:59 Comment(0)
V
0

The colorbar range can be set by passing a tuple to clim= kwarg in the pcolormesh/pcolor call as well.

plt.pcolormesh(X, Y, v, cmap=cm, clim=(-4, 4))

If the colorbar range has to be updated after the pcolormesh call, then the easiest way is plt.clim as others have mentioned. Yet another way is through the colors.Normalize object of the QuadMesh/PolyCollection object created by a pcolormesh/pcolor call.

pmesh = plt.pcolormesh(X, Y, v, cmap=cm)
pmesh.norm.autoscale([-4, 4])

On a side note, Quadmesh resides in collections of an Axes instance, so if the result of a pcolormesh was not assigned to a variable, pmesh = plt.gca().collections[0] may be used as well.

Using tom10's setup, this can be written as follows.

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = mpl.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

X, Y = np.meshgrid(*[np.arange(0, 10, .1)]*2)

data = 2*(np.sin(X) + np.sin(3*Y))

pairs = [(lambda x:x, "all"), 
         (lambda x:np.clip(x, -4, 0), "<0"), 
         (lambda x:np.clip(x, 0, 4), ">0")]


fig, axs = plt.subplots(1, 3)
for i, (f, title) in enumerate(pairs):
    pmesh = axs[i].pcolormesh(X, Y, f(data), cmap=cm, clim=(-4, 4))
    #                                                 ^^^^^^^^^^^^  <---- set vmin and vmax
    axs[i].set_title(title)
    fig.colorbar(pmesh)

fig.tight_layout();

result


Instead of having duplicate colorbars for multiple subplots, appending a single one at the right side of the figure may be "prettier". @G M already suggested a solution that does that by hard coding the colorbar boundaries but the boundaries may be set dynamically by using a function defined here.

For the example at hand, it may be written as follows.

fig, axs = plt.subplots(1, 3)
for i, (f, title) in enumerate(pairs):
    axs[i].pcolormesh(X, Y, f(data), cmap=cm, clim=(-4, 4))
    axs[i].set_title(title)

l, b, w, h = axs[-1].get_position().bounds
cax = fig.add_axes([l + w + 0.03, b, 0.03, h])
fig.colorbar(axs[-1].collections[0], cax=cax)

result2

Verbose answered 6/6, 2023 at 6:41 Comment(0)
A
0

If you're wondering how to do this with images and plt.imshow() (like I was when I found this answer), read on.

import numpy as np

from matplotlib import pyplot as plt
from matplotlib import cm
from matplotlib.colors import Normalize

width, height = 10, 10
data = np.arange(width * height).reshape((height, width))

fig, ax = plt.subplots()

# Create a Normalize object with the specified range
norm = Normalize(vmin=30, vmax=40)
ax.imshow(data, cmap="grey", norm=norm)

# Create a colorbar using the same Normalize object
plt.colorbar(cm.ScalarMappable(norm=norm, cmap="grey"), ax=ax)
plt.show()
Ashmore answered 3/4, 2024 at 13:4 Comment(0)
A
-1

Range of colorbar can be set by using instance of colorbar i.e. colorbar.ax.set_ylim(low, high) when using figure environment:

fig, ax = plt.subplots()
im = ax.contourf(...)
colorbar = fig.colorbar(im, ax= ax)
colorbar.ax.set_ylim(0, 1)
Agglutinate answered 14/6, 2023 at 17:27 Comment(1)
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?Breeder

© 2022 - 2025 — McMap. All rights reserved.