Wednesday, August 29, 2012

Nonlinear colormap in Matplotlib

One of the difficulties I deal with is data that is not evenly distributed across a particular range. Sometimes, I want to highlight a particularly small set of values with great color contrasts and leave other portions of the range to be much less distinguished. For this, I went looking for a way to have a non-linear colormap in matplotlib. To be clear, I didn't want the data itself to be transformed, rather I wanted the color mapping to progress through a normal colormap like cm.jet but expand and contract portions of the map across the entire of values to provide granular resolution at a specific range of values. The graphs below show the difference when using a linear colormap and a transformed colormap:

Image before:

Image after:

I found a script to perform this mapping. It allows the user to set a number of levels that get used to transform the colormap. My modified version of the script is below:

nlcmap - a nonlinear cmap from specified levels

Copyright (c) 2006-2007, Robert Hetland <>
Release under MIT license.

Some hacks added 2012 noted in code (@MRR)

from pylab import *
from numpy import *
from matplotlib.colors import LinearSegmentedColormap

class nlcmap(LinearSegmentedColormap):
    """A nonlinear colormap"""
    name = 'nlcmap'
    def __init__(self, cmap, levels):
        self.cmap = cmap
        # @MRR: Need to add N for backend
        self.N = cmap.N
        self.monochrome = self.cmap.monochrome
        self.levels = asarray(levels, dtype='float64')
        self._x = self.levels / self.levels.max()
        self._y = linspace(0.0, 1.0, len(self.levels))
    #@MRR Need to add **kw for 'bytes'
    def __call__(self, xi, alpha=1.0, **kw):
        """docstring for fname"""
        # @MRR: Appears broken? 
        # It appears something's wrong with the
        # dimensionality of a calculation intermediate
        #yi = stineman_interp(xi, self._x, self._y)
        yi = interp(xi, self._x, self._y)
        return self.cmap(yi, alpha)

if __name__ == '__main__':
    y, x = mgrid[0.0:3.0:100j, 0.0:5.0:100j]
    H = 50.0 * exp( -(x**2 + y**2) / 4.0 )
    levels = [0, 1, 2, 3, 6, 9, 20, 50]
    cmap_lin = cm.jet
    cmap_nonlin = nlcmap(cmap_lin, levels)
    contourf(x, y, H, levels, cmap=cmap_nonlin)
    contourf(x, y, H, levels, cmap=cmap_lin)
Some of the comments above point to changes that I had to make to get the script to work. Specifically:
  • pylab.stineman_interp gave an error relating to the dimensionality of some of the intermediate calculations
  • one of the backends requested the colormaps N member, which wasn't set in the original
  • the **kw parameter wasn't passed to the __call__ method, causing problems when optional parameters such as bytes were passed.
I'm not entirely sure if the script works correctly at this point, so use at your own risk.


  1. Dude.... I'm tots going to use this. This is awesome!