Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correcting matplotlib colorbar ticks

I've placed a color bar alongside a choropleth map. Because the data being plotted are discrete rather than continuous values, I've used a LinearSegmentedColormap (using the recipe from the scipy cookbook), which I've initialised with my max counted value + 1, in order to show a colour for 0. However, I now have two problems:

enter image description here

  1. The tick labels are incorrectly spaced (except for 5, more or less) – they should be located in the middle of the colour they identify; i.e. 0 - 4 should be moved up, and 6 - 10 should be moved down.

  2. If I initialise the colorbar with drawedges=True, so that I can style its dividers properties, I get this:

enter image description here

I'm creating my colormap and colorbar like so:

cbmin, cbmax = min(counts), max(counts)
# this normalises the counts to a 0,1 interval
counts /= np.max(np.abs(counts), axis=0)
# density is a discrete number, so we have to use a discrete color ramp/bar
cm = cmap_discretize(plt.get_cmap('YlGnBu'), int(cbmax) + 1)
mappable = plt.cm.ScalarMappable(cmap=cm)
mappable.set_array(counts)
# set min and max values for the colour bar ticks
mappable.set_clim(cbmin, cbmax)
pc = PatchCollection(patches, match_original=True)
# impose our colour map onto the patch collection
pc.set_facecolor(cm(counts))
ax.add_collection(pc,)
cb = plt.colorbar(mappable, drawedges=True)

So I'm wondering whether my converting the counts to a 0,1 interval is one of the problems.

Update :

Having tried what Hooked suggested, the 0-value is correct, but subsequent values are set progressively higher, to the the point where 9 is where 10 should be:

enter image description here

Here's the code I used:

cb = plt.colorbar(mappable)
labels = np.arange(0, int(cbmax) + 1, 1)
loc = labels + .5
cb.set_ticks(loc)
cb.set_ticklabels(labels)

And just to confirm, labels definitely has the correct values:

In [3]: np.arange(0, int(cbmax) + 1, 1)
Out[3]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
like image 599
urschrei Avatar asked Sep 09 '13 18:09

urschrei


People also ask

How do I change the tick mark on Colorbar?

Display the data as an image, i.e., on a 2D regular raster. Make a colorbar using colorbar() method with an image scalar mappable object. Set the ticks and tick labels of the colorbar using set_ticks() and set_ticklabels() methods. To display the figure, use show() method.

How do you normalize a Colorbar in Python?

norm = normi; #mpl. colors. Normalize(vmin=-80, vmax=20); plt. axis([1, 1000, -400, 400]);

How do you change the color bar label in Matplotlib?

Method 2: Change and Rotate the position of the label To rotate the colorbar labels we will use set_xticklabels() and set_yticklabels() methods for horizontal and vertical.


2 Answers

You are suffering from an off-by-one error. You have 10 ticklabels spread among 11 colors. You might be able to correct the error by using np.linspace instead of np.arange. Using np.linspace the third argument is the number of values desired. This reduces the amount of mental gymnastics needed to avoid the off-by-one error:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
import matplotlib.colors as mcolors

def colorbar_index(ncolors, cmap):
    cmap = cmap_discretize(cmap, ncolors)
    mappable = cm.ScalarMappable(cmap=cmap)
    mappable.set_array([])
    mappable.set_clim(-0.5, ncolors+0.5)
    colorbar = plt.colorbar(mappable)
    colorbar.set_ticks(np.linspace(0, ncolors, ncolors))
    colorbar.set_ticklabels(range(ncolors))

def cmap_discretize(cmap, N):
    """Return a discrete colormap from the continuous colormap cmap.

        cmap: colormap instance, eg. cm.jet. 
        N: number of colors.

    Example
        x = resize(arange(100), (5,100))
        djet = cmap_discretize(cm.jet, 5)
        imshow(x, cmap=djet)
    """

    if type(cmap) == str:
        cmap = plt.get_cmap(cmap)
    colors_i = np.concatenate((np.linspace(0, 1., N), (0.,0.,0.,0.)))
    colors_rgba = cmap(colors_i)
    indices = np.linspace(0, 1., N+1)
    cdict = {}
    for ki,key in enumerate(('red','green','blue')):
        cdict[key] = [ (indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki])
                       for i in xrange(N+1) ]
    # Return colormap object.
    return mcolors.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)

fig, ax = plt.subplots()
A = np.random.random((10,10))*10
cmap = plt.get_cmap('YlGnBu')
ax.imshow(A, interpolation='nearest', cmap=cmap)
colorbar_index(ncolors=11, cmap=cmap)    
plt.show()

enter image description here

like image 138
unutbu Avatar answered Oct 18 '22 01:10

unutbu


You can control the placement and the labels by hand. I'll start with a linear cmap generated from cmap_discretize on the page you linked:

import numpy as np
import pylab as plt

# The number of divisions of the cmap we have
k = 10

# Random test data
A = np.random.random((10,10))*k
c = cmap_discretize('jet', k)

# First show without
plt.subplot(121)
plt.imshow(A,interpolation='nearest',cmap=c)
plt.colorbar()

# Now label properly
plt.subplot(122)
plt.imshow(A,interpolation='nearest',cmap=c)

cb = plt.colorbar()
labels = np.arange(0,k,1)
loc    = labels + .5
cb.set_ticks(loc)
cb.set_ticklabels(labels)

plt.show()

enter image description here

like image 20
Hooked Avatar answered Oct 18 '22 02:10

Hooked