Associating colors from a continuous colormap to specific values in matplotlib

2.3k Views Asked by At

I am trying to find a way to associate certain data values to specific colors in a continuous colormap.

I have a certain image with values ranging from [min, max], and I would like the following values [min, q1, q2, q3, max], where q'n' refers to the quartiles, to be associated to the colors that correspond to [0, 0.25. 0.5, 0.75. 1.0] in the colormap of choice. As a result the midpoint of the coloramp would correspond to the median value in the image, and so on...

I have been looking around, but I have not been able to find a way to do this.

1

There are 1 best solutions below

1
On BEST ANSWER

You'll need to subclass matplotlib.colors.Normalize and pass in an instance of your new norm to imshow/contourf/whatever plotting function you're using.

The basic idea is illustrated in the first option here: Shifted colorbar matplotlib (Not to shill one of my own questions too much, but I can't think of another example.)

However, that question deals specifically with setting a single data value to correspond to 0.5 in the colormap. It's not too hard to expand the idea to "piecewise" normalization, though:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize

class PiecewiseNormalize(Normalize):
    def __init__(self, xvalues, cvalues):
        self.xvalues = xvalues
        self.cvalues = cvalues

        Normalize.__init__(self)

    def __call__(self, value, clip=None):
        # I'm ignoring masked values and all kinds of edge cases to make a
        # simple example...
        if self.xvalues is not None:
            x, y = self.xvalues, self.cvalues
            return np.ma.masked_array(np.interp(value, x, y))
        else:
            return Normalize.__call__(self, value, clip)

data = np.random.random((10,10))
data = 10 * (data - 0.8)

fig, ax = plt.subplots()
norm = PiecewiseNormalize([-8, -1, 0, 1.5, 2], [0, 0.1, 0.5, 0.7, 1])
im = ax.imshow(data, norm=norm, cmap=plt.cm.seismic, interpolation='none')
fig.colorbar(im)
plt.show()

enter image description here

Note that 0.5 in the colormap (white) corresponds to a data value of 0, and the red and blue regions of the colormap are asymmetric (note the broad "pink" range vs the much narrower transition to dark blue).