# Specific cmap

Hello,

How would I create a specific cmap, which would have a separate color value for each matrix value? For example, could I make a cmap that specifies 0:'red', 1:'green', 2:'yellow', etc? The linear interpolation cmaps don't work as I want them to because they normalize to the given range of the matrix.

Thank you.

edit retag close merge delete

Sort by » oldest newest most voted

I seem to recall once having a slick workaround for this but for the life of me I can't find it now. Anyway, something like this should get the job done:

# preamble
import matplotlib

# choose our preferred colour scheme, where None describes the exceptions
cdict = {0: 'red', 1: 'green', 2:'yellow', 3:'orange', 43+2*I: 'brown', None: 'purple'}

# choose some fixed order -- doesn't matter what -- for iterating the keys
ckeys, cvals = zip(*cdict.items())

# build a mapping function to turn the matrix elements into integers
# (would be faster to use a dict for lots of colours)
cmap_fn = lambda x: Integer(ckeys.index(x if x in cdict else None))

# generate a colormap with exactly the colours we want, in the right order
my_cmap = matplotlib.colors.ListedColormap(cvals)

# generate a test matrix
m = Matrix(lambda i,j: (i+j) if (i,j) != (6,6) else 43+2*I, nrows=10)

# plot it: apply the mapping function to the matrix and feed matrix_plot our colour map
matrix_plot(m.apply_map(cmap_fn), cmap=my_cmap)
 

This should generate one very ugly graph: one red cell, two green cells, three yellow cells, four orange cells, and a brown cell at (6,6) in a purple sea. It's easy to bundle this into a function if you find yourself doing it frequently.

[update: modified to make it slightly more general: now you don't need any ordering relation defined on the matrix elements]

more

Will that work even with Sage #s, like $4.3+2*I$?

( 2010-12-20 13:18:29 +0200 )edit

This is exactly what I wanted! Thank you! This should be built in to Sage.

( 2010-12-20 20:06:37 +0200 )edit

@kcrisman: yes, you just have to be careful to make sure that the matrix element still has the same type as [well, no, just 'still compares equal to'] the cdict key, else the cmap_fn will fail to find a match and fall back to the None colour.

( 2010-12-20 21:15:49 +0200 )edit

I've updated the relevant ticket to put this as an enhancement. DSM may want to put his/her real name on there to get some credit if this ever makes it into Sage, which seems like a good idea.

( 2010-12-21 09:04:18 +0200 )edit

Well, after writing this, I finally found out that we have this capability, though there are no examples in the matrix_plot doc (though it's mentioned).

sage: matrix_plot(matrix([[0,1],[1,2]]),cmap=['red','green','blue'])


This relies on get_cmap, a very nice utility I had forgotten about! Read this:

sage: sage.plot.matrix_plot.get_cmap?


Still, I'll put some other interesting resources I found below, in case that is too easy, or you need more direction for how to create good colormaps..

Here is an example of how this might be done in pure Python, and here is a nice way to 'discretize' colormaps.

Now, these don't do exactly what you want, because the colormaps seem to mostly operate on the principle of the interval $(0,1)$. If we do

sage: import matplotlib
sage: matplotlib.colors?
A module for converting numbers or color arguments to *RGB* or *RGBA*

*RGB* and *RGBA* are sequences of, respectively, 3 or 4 floats in the
range 0-1.

This module includes functions and classes for color specification
conversions, and for mapping numbers to colors in a 1-D array of
colors called a colormap. Colormapping typically involves two steps: a
data array is first mapped onto the range 0-1 using an instance of
Normalize or of a subclass; then this number in the 0-1 range is
mapped to a color using an instance of a subclass of Colormap.
Two are provided here: LinearSegmentedColormap, which is used to
generate all the built-in colormap instances, but is also useful for
making custom colormaps, and ListedColormap, which is used for
generating a custom colormap from a list of color specifications.


So here is something that might work:

sage: import matplotlib
sage: M = matplotlib.colors.ListedColormap([(0,1,1),(1,0,1),(1,1,0)],'M')
sage: matrix_plot(matrix([[0,1],[1,2]]),cmap=M)

more

( 2010-12-20 09:23:40 +0200 )edit

And I do not believe matplotlib allows one to associate specific numbers with colors, only the range. The type of cmap this provides is discrete, though, so hopefully it should be good enough if you know what values to expect ahead of time. If that is random, you may have to generate the cmap on the fly. The links provided are just some of some great posts and explanations I found on the matplotlib and Scipy websites, so hopefully you'll find what you need if this isn't it.

( 2010-12-20 09:26:14 +0200 )edit

This fits my needs if normalized, but since user input will not necessarily be in order, the other solution works better.

( 2010-12-20 20:09:04 +0200 )edit

You might check out the norm option to matrix_plot (see the docs), which takes a value argument that does away with scaling before applying a colormap. You can also specify a function that does the colormapping. See also the matplotlib documentation for colormaps and the normalizing functions.

more