使用Matplotlib创建自定义色图并绘制颜色刻度

93

我有以下问题,我想创建自己的颜色地图(红色-混合-紫色-混合-蓝色),将其映射到-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. 我不确定我的比例尺是否创建了连续(平滑)的颜色比例尺。


你能否稍微澄清一下你的问题?例如,c=指定了线条颜色,而你在谈论点。如果你真的想要点,那么你只能指定一个markerfacecolor,使用scatter可能是更好的选择。而且确实ListedColormap是列出的,不是连续的,可以看看LinearSegmentedColormap - Rutger Kassies
很奇怪,它应该是点,看起来也像点。 - Trollbrot
当然可以,但这是你需要澄清的事情。我们无法看到你正在使用哪种绘图样式。如果你使用 plt.plot(values, 'o'),你将只绘制标记而没有线条,但标记将具有一个固定的颜色,它不会(也不能)随着值的变化而改变。 - Rutger Kassies
4个回答

127

由于其他回答中使用的方法对于这样简单的任务似乎相当复杂,这里提供了一个新的答案:

你可以使用LinearSegmentedColormap而不是产生离散颜色映射的ListedColormap。 这可以使用from_list 方法轻松创建。

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()

这里输入图像描述


更一般地,如果您有一个值列表(例如[-2.,-1,2])和相应的颜色列表(例如["red","violet","blue"]),使得第n个值应对应于第n个颜色,则可以将值标准化并将其作为元组提供给from_list方法。

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()

在此输入图片描述


1
现在,您如何将自定义的范围传递给它?例如,red 对应于 -5violet 对应于 1blue 对应于 100。如果您能查看我在这里提出的问题,我将不胜感激。 - Cleb
2
使用相应绘图方法的vminvmaxnorm参数。 - ImportanceOfBeingErnest
3
这可能没有完全定制地图中所接受的答案那么灵活,但那太过复杂了,而我认为这个答案正是大多数人在想要自定义颜色映射时所需要的。 - BjornW
1
@Notso LinearSegmentedColormap.from_list("", [(0,"red"), (.1,"violet"), (.5, "blue"), (1.0, "green")])。我更新了答案,希望这样更清楚明白。 - ImportanceOfBeingErnest
1
@ShaunLowis 在这种特定情况下,您可以自己插值RGB值。更一般地说,您可以先创建一个颜色映射,并在非等距步骤中取映射的颜色。例如,如果cmap是原始的颜色映射,则cvals=[-12, -2,0,2,12]; colors = cmap(plt.Normalize(-12,12)(np.array(cvals)))将为您提供挤压映射的新颜色。 - ImportanceOfBeingErnest
显示剩余9条评论

95
这里有一个说明性的例子如何创建自定义色图。 docstring对于理解cdict的含义至关重要。一旦你掌握了这个,你可以像这样使用cdict:
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
          }

尽管 cdict 格式给了你很大的灵活性,但我发现对于简单的渐变,它的格式相当不直观。这里有一个实用函数来帮助生成简单的线性分段调色板:
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


顺便说一下,for循环
for i in range(0, len(array_dg)):
  plt.plot(array_dg[i], markers.next(),alpha=alpha[i], c=colors.next())

每次调用plt.plot将为一个点绘制一个图表。这适用于少量的点,但对于许多点来说,速度会变得极其缓慢。 plt.plot只能绘制一种颜色,但是plt.scatter可以为每个点分配不同的颜色。因此,使用plt.scatter是正确的方式。

为什么我不能在plt.set_cmap()中使用这个神奇函数创建的颜色映射?错误信息非常长,最后一行是ValueError:Colormap CustomMap未被识别。 - Phlya
3
首先注册颜色映射:plt.register_cmap(name=rvb.name, cmap=rvb),然后调用plt.set_cmap(rvb) - unutbu
有没有关于如何在plt.cm中使用它的建议?我希望通过xy的比率来给我的散点图上色。之前我使用的是color = plt.cm.cool(x/y)ax.scatter(x,y,c=colors) - As3adTintin
1
@As3adTintin:上面的rvb是一个完整的颜色映射,就像plt.cm.cool一样。因此它们是可互换的:color = rvb(x/y) - unutbu
@unutbu 哦,就在那里...不管怎样,非常感谢你的回答! - As3adTintin
显示剩余2条评论

14
如果您想自动创建常用于表面绘图的定制发散色图,那么结合@unutbu方法使用此模块对我很有帮助。
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])

高值和低值可以是字符串颜色名称或RGB元组。这是使用surface plot demo的结果: enter image description here

4
这对我来说似乎有效。
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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接