interpolation and colormaps, over and under colors

Hi,

I am plotting with imshow() and interpolation is turned on
('gaussian'). Part of my issue is that the distribution of values is
such that I need to set the over/under colors to grab the most
relevant values. If I set the over color to be the "maximum" color,
then the result is too dark. Conversely for the under color. I can
set the over/under colors to not draw, but then I have "holes" in my
image. Is it possible to incorporate interpolation? That is, I would
like to set my over color to be interpolated among its neighboring
cells. This seems like it would require an (more) "intelligent"
colormap, so my guess is that this isn't currently supported. I'm
growing more comfortable with the mpl internals and might give a stab
at implementing this, but I'm not quite sure where to start.

Thanks in advance.

I'm not sure I understand the problem; could you provide a tiny example to illustrate?

Eric

···

On 05/27/2010 09:32 AM, T J wrote:

Hi,

I am plotting with imshow() and interpolation is turned on
('gaussian'). Part of my issue is that the distribution of values is
such that I need to set the over/under colors to grab the most
relevant values. If I set the over color to be the "maximum" color,
then the result is too dark. Conversely for the under color. I can
set the over/under colors to not draw, but then I have "holes" in my
image. Is it possible to incorporate interpolation? That is, I would
like to set my over color to be interpolated among its neighboring
cells. This seems like it would require an (more) "intelligent"
colormap, so my guess is that this isn't currently supported. I'm
growing more comfortable with the mpl internals and might give a stab
at implementing this, but I'm not quite sure where to start.

Thanks in advance.

Sure, let me focus just on the interpolation and I'll leave the
filtering issue out.

In the script below, I plot a 3x3 array with the center element having
an "over" value. In the default case, because its value is over, the
colormap will assign it the maximum color. I also plot the case when
the "over" color is explicitly set to the minimum color and also to
white. What I want is this:

   The center element should be an equal mixture of the 4 elements around it.

This is partially achieved with "white" (and I suppose I could pick
"grey" or "black"), but I think it might be nicer if it were a pure
mixture, rather than a mixture of the surrounding colors and the
"over" color.

The script is attached below. Sorry it is a bit long, but I needed a
discrete colormap. Can we get cmap_discrete() into matplotlib?

···

On Thu, May 27, 2010 at 3:23 PM, Eric Firing <efiring@...202...> wrote:

I'm not sure I understand the problem; could you provide a tiny example
to illustrate?

------------------

import matplotlib.pyplot as plt
import matplotlib.colors

import numpy as np
from scipy import interpolate

#### http://www.scipy.org/Cookbook/Matplotlib/ColormapTransformations
#### Can this be added to matplotlib?
def cmap_discretize(cmap, N):
    """Return a discrete colormap from the continuous colormap cmap.

        cmap: colormap instance, eg. cm.jet.
        N: Number of colors.

    Example
        x = resize(arange(100), (5,100))
        djet = cmap_discretize(cm.jet, 5)
        imshow(x, cmap=djet)
    """

    cdict = cmap._segmentdata.copy()
    # N colors
    colors_i = np.linspace(0,1.,N)
    # N+1 indices
    indices = np.linspace(0,1.,N+1)
    for key in ('red','green','blue'):
        # Find the N colors
        D = np.array(cdict[key])
        I = interpolate.interp1d(D[:,0], D[:,1])
        colors = I(colors_i)
        # Place these colors at the correct indices.
        A = np.zeros((N+1,3), float)
        A[:,0] = indices
        A[1:,1] = colors
        A[:-1,2] = colors
        # Create a tuple for the dictionary.
        L = []
        for l in A:
            L.append(tuple(l))
        cdict[key] = tuple(L)
    # Return colormap object.
    return matplotlib.colors.LinearSegmentedColormap('colormap',cdict,1024)

def draw(m, cm, norm, ncolors):
    ax = plt.gca()
    ai = ax.imshow(m, cmap=cm, norm=norm, interpolation='gaussian')
    cb = ax.figure.colorbar(ai)
    cb.set_ticks(np.linspace(.5, ncolors-.5, ncolors))
    cb.set_ticklabels(['$%s$' % (i,) for i in np.arange(ncolors)])
    return ai, cb

if __name__ == '__main__':
    ncolors = 4
    norm = plt.Normalize(vmax=ncolors)
    m = np.array([[0, 0, 1],
                  [3, 10, 1],
                  [3, 2, 2]])

    for over in [None, 'min', (1,1,1,1)]:
        f = plt.figure()
        cm = cmap_discretize(plt.cm.jet, ncolors)
        if over == 'min':
            cm.set_over(cm(0.0))
        elif over is not None:
            cm.set_over(over)
        ai, cb = draw(m, cm, norm, ncolors)

    plt.show()

I'm not sure I understand the problem; could you provide a tiny example
to illustrate?

Sure, let me focus just on the interpolation and I'll leave the
filtering issue out.

In the script below, I plot a 3x3 array with the center element having
an "over" value. In the default case, because its value is over, the
colormap will assign it the maximum color. I also plot the case when
the "over" color is explicitly set to the minimum color and also to
white. What I want is this:

    The center element should be an equal mixture of the 4 elements around it.

You can't do this via any manipulation of the colormap, or any fancier colormap specification--instead, you have to manipulate the data value. For example, you could identify the "over" values in your data, and then use 2-D interpolation to replace them with the values you want.

Basemap includes a 2-D interpolation routine:

from mpl_toolkits.basemap import interp

This is partially achieved with "white" (and I suppose I could pick
"grey" or "black"), but I think it might be nicer if it were a pure
mixture, rather than a mixture of the surrounding colors and the
"over" color.

The script is attached below. Sorry it is a bit long, but I needed a
discrete colormap. Can we get cmap_discrete() into matplotlib?

No, because it doesn't make much sense, given the mpl paradigm in which a colormap and a norm work together. If you want 4 colors, make a colormap with 4 colors, and use a norm that maps your data to those 4 colors.

For example:

cm4 = get_cmap('jet', 4)
cm4a = mpl.colors.ListedColormap(get_cmap('jet', 256)([20, 70, 150, 200]))

You can select any discrete set of colors you want using ListedColormap.

Then you can use the default Normalize, or a custom BoundaryNorm, to map data ranges to the colors. You just don't need a lookup table with 1024 entries to specify 4 colors--it doesn't gain you anything.

Eric

···

On 05/27/2010 01:58 PM, T J wrote:

On Thu, May 27, 2010 at 3:23 PM, Eric Firing<efiring@...202...> wrote:

------------------

import matplotlib.pyplot as plt
import matplotlib.colors

import numpy as np
from scipy import interpolate

#### http://www.scipy.org/Cookbook/Matplotlib/ColormapTransformations
#### Can this be added to matplotlib?
def cmap_discretize(cmap, N):
     """Return a discrete colormap from the continuous colormap cmap.

         cmap: colormap instance, eg. cm.jet.
         N: Number of colors.

     Example
         x = resize(arange(100), (5,100))
         djet = cmap_discretize(cm.jet, 5)
         imshow(x, cmap=djet)
     """

     cdict = cmap._segmentdata.copy()
     # N colors
     colors_i = np.linspace(0,1.,N)
     # N+1 indices
     indices = np.linspace(0,1.,N+1)
     for key in ('red','green','blue'):
         # Find the N colors
         D = np.array(cdict[key])
         I = interpolate.interp1d(D[:,0], D[:,1])
         colors = I(colors_i)
         # Place these colors at the correct indices.
         A = np.zeros((N+1,3), float)
         A[:,0] = indices
         A[1:,1] = colors
         A[:-1,2] = colors
         # Create a tuple for the dictionary.
         L = []
         for l in A:
             L.append(tuple(l))
         cdict[key] = tuple(L)
     # Return colormap object.
     return matplotlib.colors.LinearSegmentedColormap('colormap',cdict,1024)

def draw(m, cm, norm, ncolors):
     ax = plt.gca()
     ai = ax.imshow(m, cmap=cm, norm=norm, interpolation='gaussian')
     cb = ax.figure.colorbar(ai)
     cb.set_ticks(np.linspace(.5, ncolors-.5, ncolors))
     cb.set_ticklabels(['$%s$' % (i,) for i in np.arange(ncolors)])
     return ai, cb

if __name__ == '__main__':
     ncolors = 4
     norm = plt.Normalize(vmax=ncolors)
     m = np.array([[0, 0, 1],
                   [3, 10, 1],
                   [3, 2, 2]])

     for over in [None, 'min', (1,1,1,1)]:
         f = plt.figure()
         cm = cmap_discretize(plt.cm.jet, ncolors)
         if over == 'min':
             cm.set_over(cm(0.0))
         elif over is not None:
             cm.set_over(over)
         ai, cb = draw(m, cm, norm, ncolors)

     plt.show()

You can't do this via any manipulation of the colormap, or any fancier
colormap specification--instead, you have to manipulate the data value. For
example, you could identify the "over" values in your data, and then use 2-D
interpolation to replace them with the values you want.

Basemap includes a 2-D interpolation routine:

from mpl_toolkits.basemap import interp

Thanks!

This is partially achieved with "white" (and I suppose I could pick
"grey" or "black"), but I think it might be nicer if it were a pure
mixture, rather than a mixture of the surrounding colors and the
"over" color.

The script is attached below. Sorry it is a bit long, but I needed a
discrete colormap. Can we get cmap_discrete() into matplotlib?

No, because it doesn't make much sense, given the mpl paradigm in which a
colormap and a norm work together. If you want 4 colors, make a colormap
with 4 colors, and use a norm that maps your data to those 4 colors.

For example:

cm4 = get_cmap('jet', 4)
cm4a = mpl.colors.ListedColormap(get_cmap('jet', 256)([20, 70, 150, 200]))

You can select any discrete set of colors you want using ListedColormap.

Then you can use the default Normalize, or a custom BoundaryNorm, to map
data ranges to the colors. You just don't need a lookup table with 1024
entries to specify 4 colors--it doesn't gain you anything.

Wonderful. Definitely makes the cookbook entry seem unnecessary

···

On Thu, May 27, 2010 at 5:44 PM, Eric Firing <efiring@...202...> wrote: