I would like to create a colormap that plots a particular

> value in a specific color but is otherwise normal. So far

> I haven't been smart enough to figure out how to do

> this. For example, imagine an image with missing data. The

> missing data has some sentinel value e.g. -1, which I can

> set to be outside the range of the rest of the image. I

> want the missing data to plot is a specific color unrelated

> to the colormap for the rest of the image. Like I said, I

> haven't been clever enough to figure it out. This is sort

> of similar behavior to masked arrays in pcolor, but I would

> like to use imshow if possible. Perhaps a custom

> normalization to compress a given colormap and map the

> sentinel to a boundary value which has discontinuous

> segment? Any ideas would be appreciated.

This is a nice example. At first I thought it would be easier since

all one needs to do is define a custom norm and cmap instance to

imshow. All these really need to be is callable, but, but because of

my &$*%*!#@ C++ background, I introduced typechecking in the imshow

code

if norm is not None: assert(isinstance(norm, normalize))

if cmap is not None: assert(isinstance(cmap, Colormap))

So there is a little extra overhead to make them the right "type". It

would have been preferable to use ducktyping here.

The norm instance takes your data and returns a same shape float array

ranging normalized to [0,1]. We want to define our own norm instance

that preserves the sentinel

import pylab

import matplotlib.numerix as nx

import matplotlib.colors

class DannysNorm(matplotlib.colors.normalize):

"""

Leave the sentinel unchanged

"""

def __init__(self, sentinel):

matplotlib.colors.normalize.__init__(self)

self.sentinel = sentinel

def __call__(self, value):

vnorm = matplotlib.colors.normalize.__call__(self, value)

return nx.where(value==self.sentinel, self.sentinel, vnorm)

Next you have to define a custom colormap. The cmap instance takes

normalized data and returns RGBA data, and the call method is

def __call__(self, X, alpha=1):

So we need to create a class which takes a default cmap and returns it

except at the sentinel value

class DannysMap(matplotlib.colors.Colormap):

def __init__(self, cmap, sentinel, rgb):

self.N = cmap.N

self.name = 'DannysMap'

self.cmap = cmap

self.sentinel = sentinel

if len(rgb)!=3:

raise ValueError('sentinel color must be RGB')

self.rgb = rgb

def __call__(self, X, alpha=1):

m,n = X.shape

r,g,b = self.rgb

Xm = self.cmap(X)

ret = nx.zeros( (m,n,4), typecode=nx.Float)

ret[:,:,0] = nx.where(X==self.sentinel, r, Xm[:,:,0])

ret[:,:,1] = nx.where(X==self.sentinel, g, Xm[:,:,1])

ret[:,:,2] = nx.where(X==self.sentinel, b, Xm[:,:,2])

ret[:,:,3] = nx.where(X==self.sentinel, alpha, Xm[:,:,3])

return ret

That's it. Now you just have to pass these on to imshow

X = nx.mlab.rand(100,50)

X[20:30, 5:10] = -1

cmap = DannysMap(pylab.cm.jet, -1, (1,0,0))

norm = DannysNorm(-1)

pylab.imshow(X, cmap = cmap, norm=norm)

pylab.show()

JDH