99

I have the following problem, I want to create my own colormap (red-mix-violet-mix-blue) that maps to values between -2 and +2 and want to use it to color points in my plot. The plot should then have the colorscale to the right.

That is how I create the map so far. But I am not really sure if it mixes the colors.

cmap = matplotlib.colors.ListedColormap(["red","violet","blue"], name='from_list', N=None)
m = cm.ScalarMappable(norm=norm, cmap=cmap)


That way I map the colors to the values.

colors = itertools.cycle([m.to_rgba(1.22), ..])


Then I plot it:

for i in range(0, len(array_dg)):
  plt.plot(array_dg[i], markers.next(),alpha=alpha[i], c=colors.next())


My problems are:
1. I can't plot the color scale.
2. I am not completely sure if my scale is creating a continues (smooth) colorscale.

3
  • Could you clarify your question a bit? For example, c= specifies the line color, while you are talking about points. You can only specify one markerfacecolor, scatter might be a better option if you really want points. And indeed ListedColormap is listed, not continuous, see LinearSegmentedColormap. Commented May 30, 2013 at 11:44
  • That is strange, it is supposed to be points and it looks like points. Commented May 30, 2013 at 11:47
  • You can off course, but thats what you should clarify. We cant see what plot style you are using. If you use plt.plot(values, 'o'), you will plot only markers and no line, but the markers will have one fixed color which doesnt (and cant) vary by the value. Commented May 30, 2013 at 12:25

4 Answers 4

137

Since the methods used in other answers seems quite complicated for such easy task, here is a new answer:

Instead of a ListedColormap, which produces a discrete colormap, you may use a LinearSegmentedColormap. This can easily be created from a list using the from_list method.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

x,y,c = zip(*np.random.rand(30,3)*4-2)

norm=plt.Normalize(-2,2)
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["red","violet","blue"])

plt.scatter(x,y,c=c, cmap=cmap, norm=norm)
plt.colorbar()
plt.show()

enter image description here


More generally, if you have a list of values (e.g. [-2., -1, 2]) and corresponding colors, (e.g. ["red","violet","blue"]), such that the nth value should correspond to the nth color, you can normalize the values and supply them as tuples to the from_list method.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

x,y,c = zip(*np.random.rand(30,3)*4-2)

cvals  = [-2., -1, 2]
colors = ["red","violet","blue"]

norm=plt.Normalize(min(cvals),max(cvals))
tuples = list(zip(map(norm,cvals), colors))
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", tuples)

plt.scatter(x,y,c=c, cmap=cmap, norm=norm)
plt.colorbar()
plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

14 Comments

How would you now pass an own defined range to this, e.g. that red corresponds to -5, violet to 1 and blue to 100? I would very much appreciate if you could look at the question I asked here.
Using the vmin and vmax or the norm argument of the respective plotting method.
This might not be as flexible as the complete custom map in the accepted answer, but that is crazily complicated and this answer is exactly what most people need when they want a custom colormap I think.
@Notso LinearSegmentedColormap.from_list("", [(0,"red"), (.1,"violet"), (.5, "blue"), (1.0, "green")]). I updated the answer to hopefully make this clearer.
@ShaunLowis In this specific case you can just interpolate the RGB values yourself. More generally, you may create a colormap first and take the colors of the map, at non-equidistant steps. E.g. if cmap is the original colormap, then cvals=[-12, -2,0,2,12]; colors = cmap(plt.Normalize(-12,12)(np.array(cvals))) will give you the new colors for the squeezed map.
|
96

There is an illustrative example of how to create custom colormaps here. The docstring is essential for understanding the meaning of cdict. Once you get that under your belt, you might use a cdict like this:

cdict = {'red':   ((0.0, 1.0, 1.0), 
                   (0.1, 1.0, 1.0),  # red 
                   (0.4, 1.0, 1.0),  # violet
                   (1.0, 0.0, 0.0)), # blue

         'green': ((0.0, 0.0, 0.0),
                   (1.0, 0.0, 0.0)),

         'blue':  ((0.0, 0.0, 0.0),
                   (0.1, 0.0, 0.0),  # red
                   (0.4, 1.0, 1.0),  # violet
                   (1.0, 1.0, 0.0))  # blue
          }

Although the cdict format gives you a lot of flexibility, I find for simple gradients its format is rather unintuitive. Here is a utility function to help generate simple LinearSegmentedColormaps:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors


def make_colormap(seq):
    """Return a LinearSegmentedColormap
    seq: a sequence of floats and RGB-tuples. The floats should be increasing
    and in the interval (0,1).
    """
    seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
    cdict = {'red': [], 'green': [], 'blue': []}
    for i, item in enumerate(seq):
        if isinstance(item, float):
            r1, g1, b1 = seq[i - 1]
            r2, g2, b2 = seq[i + 1]
            cdict['red'].append([item, r1, r2])
            cdict['green'].append([item, g1, g2])
            cdict['blue'].append([item, b1, b2])
    return mcolors.LinearSegmentedColormap('CustomMap', cdict)


c = mcolors.ColorConverter().to_rgb
rvb = make_colormap(
    [c('red'), c('violet'), 0.33, c('violet'), c('blue'), 0.66, c('blue')])
N = 1000
array_dg = np.random.uniform(0, 10, size=(N, 2))
colors = np.random.uniform(-2, 2, size=(N,))
plt.scatter(array_dg[:, 0], array_dg[:, 1], c=colors, cmap=rvb)
plt.colorbar()
plt.show()

enter image description here


By the way, the for-loop

for i in range(0, len(array_dg)):
  plt.plot(array_dg[i], markers.next(),alpha=alpha[i], c=colors.next())

plots one point for every call to plt.plot. This will work for a small number of points, but will become extremely slow for many points. plt.plot can only draw in one color, but plt.scatter can assign a different color to each dot. Thus, plt.scatter is the way to go.

7 Comments

Now I got a problem. I also would like to get a different marker symbol according to the color (I have 13 different colors). But the scatter plot allows only one marker per plot, or do I miss something?
In that case you will need to call plt.scatter (or plt.plot) once for each color/marker combination.
Why can't I use a color map created with this awesome function in plt.set_cmap()? The error is very long, the last line is ValueError: Colormap CustomMap is not recognized.
@Ilya: First register the colormap: plt.register_cmap(name=rvb.name, cmap=rvb) and then call plt.set_cmap(rvb).
@As3adTintin: rvb above is a full-fledge Colormap, just like plt.cm.cool. So they are fungible: color = rvb(x/y).
|
14

If you want to automate the creating of a custom divergent colormap commonly used for surface plots, this module combined with @unutbu method worked well for me.

def diverge_map(high=(0.565, 0.392, 0.173), low=(0.094, 0.310, 0.635)):
    '''
    low and high are colors that will be used for the two
    ends of the spectrum. they can be either color strings
    or rgb color tuples
    '''
    c = mcolors.ColorConverter().to_rgb
    if isinstance(low, basestring): low = c(low)
    if isinstance(high, basestring): high = c(high)
    return make_colormap([low, c('white'), 0.5, c('white'), high])

The high and low values can be either string color names or rgb tuples. This is the result using the surface plot demo: enter image description here

Comments

4

This seems to work for me.

def make_Ramp( ramp_colors ): 
    from colour import Color
    from matplotlib.colors import LinearSegmentedColormap

    color_ramp = LinearSegmentedColormap.from_list( 'my_list', [ Color( c1 ).rgb for c1 in ramp_colors ] )
    plt.figure( figsize = (15,3))
    plt.imshow( [list(np.arange(0, len( ramp_colors ) , 0.1)) ] , interpolation='nearest', origin='lower', cmap= color_ramp )
    plt.xticks([])
    plt.yticks([])
    return color_ramp

custom_ramp = make_Ramp( ['#754a28','#893584','#68ad45','#0080a5' ] ) 

custom color ramp

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.