I am trying to make a discrete colorbar for a scatterplot in matplotlib
I have my x, y data and for each point an integer tag value which I want to be represented with a unique colour, e.g.
plt.scatter(x, y, c=tag)
typically tag will be an integer ranging from 0-20, but the exact range may change
so far I have just used the default settings, e.g.
plt.colorbar()
which gives a continuous range of colours. Ideally i would like a set of n discrete colours (n=20 in this example). Even better would be to get a tag value of 0 to produce a gray colour and 1-20 be colourful.
I have found some ‘cookbook’ scripts but they are very complicated and I cannot think they are the right way to solve a seemingly simple problem
Advertisement
Answer
You can create a custom discrete colorbar quite easily by using a BoundaryNorm as normalizer for your scatter. The quirky bit (in my method) is making 0 showup as grey.
For images i often use the cmap.set_bad() and convert my data to a numpy masked array. That would be much easier to make 0 grey, but i couldnt get this to work with the scatter or the custom cmap.
As an alternative you can make your own cmap from scratch, or read-out an existing one and override just some specific entries.
import numpy as np import matplotlib as mpl import matplotlib.pylab as plt fig, ax = plt.subplots(1, 1, figsize=(6, 6)) # setup the plot x = np.random.rand(20) # define the data y = np.random.rand(20) # define the data tag = np.random.randint(0, 20, 20) tag[10:12] = 0 # make sure there are some 0 values to show up as grey cmap = plt.cm.jet # define the colormap # extract all colors from the .jet map cmaplist = [cmap(i) for i in range(cmap.N)] # force the first color entry to be grey cmaplist[0] = (.5, .5, .5, 1.0) # create the new map cmap = mpl.colors.LinearSegmentedColormap.from_list( 'Custom cmap', cmaplist, cmap.N) # define the bins and normalize bounds = np.linspace(0, 20, 21) norm = mpl.colors.BoundaryNorm(bounds, cmap.N) # make the scatter scat = ax.scatter(x, y, c=tag, s=np.random.randint(100, 500, 20), cmap=cmap, norm=norm) # create a second axes for the colorbar ax2 = fig.add_axes([0.95, 0.1, 0.03, 0.8]) cb = plt.colorbar.ColorbarBase(ax2, cmap=cmap, norm=norm, spacing='proportional', ticks=bounds, boundaries=bounds, format='%1i') ax.set_title('Well defined discrete colors') ax2.set_ylabel('Very custom cbar [-]', size=12)
I personally think that with 20 different colors its a bit hard to read the specific value, but thats up to you of course.