使用 matplotlib 和绘图颜色比例创建自己的颜色图

我有以下问题,我想创建我自己的颜色图(红色-混合-紫色-混合-蓝色) ,映射到值之间的 -2和 + 2,并希望使用它的颜色点在我的情节。 然后情节应该有右边的颜色比例。

到目前为止,我就是这样创建地图的。但是我不确定它是否混合了颜色。

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


这样我将颜色映射到值。

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


然后我画出来:

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


我的问题是:
1. 我无法绘制颜色比例。
2. 我不能完全确定我的比例尺是否正在创建一个连续(平滑)的颜色比例尺。

156999 次浏览

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.

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

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

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