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 <hetland@tamu.edu> 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) subplot(2,1,1) contourf(x, y, H, levels, cmap=cmap_nonlin) colorbar() subplot(2,1,2) contourf(x, y, H, levels, cmap=cmap_lin) colorbar() savefig('nlcmap_example.png')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.
Dude.... I'm tots going to use this. This is awesome!
ReplyDeletePerfect! Thank you so much :)
ReplyDelete