从Matplotlib的颜色映射中获取单个颜色

266
如果您有一个名为cmap色彩映射,例如:
cmap = matplotlib.cm.get_cmap('Spectral')

如何在0和1之间获取特定的颜色,其中0是地图中的第一种颜色,1是地图中的最后一种颜色?

理想情况下,我可以通过执行以下操作来获取地图中的中间颜色:

>>> do_some_magic(cmap, 0.5) # Return an RGBA tuple
(0.1, 0.2, 0.3, 1.0)
9个回答

400
你可以使用下面的代码来实现,你所提供的问题中的代码实际上已经非常接近你需要的内容了,你只需要调用你所拥有的对象即可。
import matplotlib

cmap = matplotlib.cm.get_cmap('Spectral')

rgba = cmap(0.5)
print(rgba) # (0.99807766255210428, 0.99923106502084169, 0.74602077638401709, 1.0)
对于范围在[0.0, 1.0]之外的值,它将返回下限和上限颜色(分别为最小值和最大值)。默认情况下,这是范围内的最小和最大颜色(即0.0和1.0)。可以使用和更改此默认设置。
对于诸如和之类的“特殊”数字,默认值为使用0.0值,这可以使用进行更改,类似于上述的下限和上限。
最后,您可能需要对数据进行归一化,使其符合区间[0.0, 1.0]。这可以使用 matplotlib.colors.Normalize 轻松完成,就像下面的示例中所示,其中参数vmin和vmax描述应映射到0.0和1.0的数字。
import matplotlib

norm = matplotlib.colors.Normalize(vmin=10.0, vmax=20.0)

print(norm(15.0)) # 0.5
一个对于数据范围具有大量值的情况下可用的对数归一化器 (matplotlib.colors.LogNorm) 也是可获得的。 (感谢Joe Kingtontcaswell提供了改进答案的建议。)

4
实际上,对于小于0或大于1的值,它将返回“过低”或“过高”的颜色。默认情况下,它是色图底部/顶部的颜色,但可以更改。例如:cmap.set_under('red'); print cmap(0.0), cmap(-0.01) - Joe Kington
24
非常有用的信息,为什么这在文档中却找不到呢?! - Jaap Eldering
20
如果对于任何人来说这个方法都不起作用,并且你看到了 module 'matplotlib' has no attribute 'cm',请尝试将前两行替换为 import matplotlib.pyplot as plt; cmap = plt.cm.get_cmap('Spectral') - pretzlstyle
normalize 有没有类似于 set_bad 的等效函数? - akozi
3
由于这种方式已经过时了,这里提供一个最新的方法: import matplotlib cmap = matplotlib.colormaps["Spectral"] - Gleb Ryabov
显示剩余8条评论

24

为了获得rgba的整数值而不是浮点数值,我们可以执行以下操作:

rgba = cmap(0.5,bytes=True)

所以根据Ffisegydd的回答,为了简化代码,代码应该像这样:

#import colormap
from matplotlib import cm

#normalize item number values to colormap
norm = matplotlib.colors.Normalize(vmin=0, vmax=1000)

#colormap possible values = viridis, jet, spectral
rgba_color = cm.jet(norm(400),bytes=True) 

#400 is one of value between 0 and 1000

14

我曾遇到类似的情况,在这种情况下,我需要从一个颜色地图中获取“n”个颜色,以便将每个颜色分配给我的数据。 我在名为“mycolorpy”的软件包中编写了一段代码。 您可以使用以下命令通过 pip 安装它:

pip install mycolorpy

您可以执行以下操作:
from mycolorpy import colorlist as mcp
import numpy as np

示例:从cmap“ winter”创建一个包含5个十六进制字符串的列表

color1=mcp.gen_color(cmap="winter",n=5)
print(color1)

输出:

['#0000ff', '#0040df', '#0080bf', '#00c09f', '#00ff80']

从 cmap bwr 中生成 16 种颜色列表的另一个示例:

color2=mcp.gen_color(cmap="bwr",n=16)
print(color2)

输出:

['#0000ff', '#2222ff', '#4444ff', '#6666ff', '#8888ff', '#aaaaff', '#ccccff', '#eeeeff', '#ffeeee', '#ffcccc', '#ffaaaa', '#ff8888', '#ff6666', '#ff4444', '#ff2222', '#ff0000']

这里有一个python笔记本,其中包含使用示例以更好地可视化。

比如你想要从一个经过归一化的数据生成颜色列表。你可以使用以下代码:

a=random.randint(1000, size=(200))
a=np.array(a)
color1=mcp.gen_color_normalized(cmap="seismic",data_arr=a)
plt.scatter(a,a,c=color1)

输出结果: 这里输入图片描述

你还可以使用以下方式颠倒颜色:

color1=mcp.gen_color_normalized(cmap="seismic",data_arr=a,reverse=True)
plt.scatter(a,a,c=color1)

输出: 这里输入图片描述


2
这并没有回答楼主的问题,因为已经有一个优秀的批准答案了。此外,已经有一些工具可以很好地执行您描述的过程,例如CMasher(https://cmasher.readthedocs.io/index.html)。 - 1313e
2
@1313e:这个“优秀的批准答案”不再起作用了。就是这样。不过感谢你提供这个优秀的库的链接。 - Eric Duminil
2
非常感谢!救了我的一天。 - seizethedata

3

我曾经遇到类似的问题,但我需要在连续的图中使用高度对比的颜色。我还制作了一个包含参考数据的公共子图的图表,因此我希望颜色序列是一致可重复的。

最初我尝试随机生成颜色,并在每个绘图之前重新设置随机数种子。这样做效果还行(在下面的代码中被注释掉),但可能会生成几乎无法区分的颜色。我想要高度对比的颜色,最好从包含所有颜色的色图中采样。

我可以在单个图中拥有多达31个数据系列,因此我将色图划分为那么多步骤。然后按照一定的顺序走这些步骤,以确保我不会很快返回给定的颜色附近。

我的数据是高度不规则的时间序列,因此我想同时看到点和线,其中点具有与线相反的颜色。

考虑到上述所有因素,最容易的方法是生成一个包含绘制各个系列所需参数的字典,然后将其作为调用的一部分进行扩展。

以下是我的代码。也许不太美观,但功能正常。

from matplotlib import cm
cmap = cm.get_cmap('gist_rainbow')  #('hsv') #('nipy_spectral')

max_colors = 31   # Constant, max mumber of series in any plot.  Ideally prime.
color_number = 0  # Variable, incremented for each series.

def restart_colors():
    global color_number
    color_number = 0
    #np.random.seed(1)

def next_color():
    global color_number
    color_number += 1
    #color = tuple(np.random.uniform(0.0, 0.5, 3))
    color = cmap( ((5 * color_number) % max_colors) / max_colors )
    return color

def plot_args():  # Invoked for each plot in a series as: '**(plot_args())'
    mkr = next_color()
    clr = (1 - mkr[0], 1 - mkr[1], 1 - mkr[2], mkr[3])  # Give line inverse of marker color
    return {
        "marker": "o",
        "color": clr,
        "mfc": mkr,
        "mec": mkr,
        "markersize": 0.5,
        "linewidth": 1,
    }

我的背景是JupyterLab和Pandas,这里是样本绘图代码:

restart_colors()  # Repeatable color sequence for every plot

fig, axs = plt.subplots(figsize=(15, 8))
plt.title("%s + T-meter"%name)

# Plot reference temperatures:
axs.set_ylabel("°C", rotation=0)
for s in ["T1", "T2", "T3", "T4"]:
    df_tmeter.plot(ax=axs, x="Timestamp", y=s, label="T-meter:%s" % s, **(plot_args()))

# Other series gets their own axis labels
ax2 = axs.twinx()
ax2.set_ylabel(units)

for c in df_uptime_sensors:
    df_uptime[df_uptime["UUID"] == c].plot(
        ax=ax2, x="Timestamp", y=units, label="%s - %s" % (units, c), **(plot_args())
    )

fig.tight_layout()
plt.show()

生成的图表可能不是最佳示例,但当进行交互式缩放时,它变得更加相关。 uptime + T-meter

1

色图具有自己的归一化方法,因此如果您已经制作了一个绘图,您可以访问某个值处的颜色。

import matplotlib.pyplot as plt
import numpy as np

cmap = plt.cm.viridis

cm = plt.pcolormesh(np.random.randn(10, 10), cmap=cmap)

print(cmap(cm.norm(2.2)))

1

Ffisegyddamaliammr的解决方案基础上,这里有一个示例,我们为自定义颜色映射制作CSV表示:

#! /usr/bin/env python3
import matplotlib
import numpy as np 

vmin = 0.1
vmax = 1000

norm = matplotlib.colors.Normalize(np.log10(vmin), np.log10(vmax))
lognum = norm(np.log10([.5, 2., 10, 40, 150,1000]))

cdict = {
    'red':
    (
        (0., 0, 0),
        (lognum[0], 0, 0),
        (lognum[1], 0, 0),
        (lognum[2], 1, 1),
        (lognum[3], 0.8, 0.8),
        (lognum[4], .7, .7),
    (lognum[5], .7, .7)
    ),
    'green':
    (
        (0., .6, .6),
        (lognum[0], 0.8, 0.8),
        (lognum[1], 1, 1),
        (lognum[2], 1, 1),
        (lognum[3], 0, 0),
        (lognum[4], 0, 0),
    (lognum[5], 0, 0)
    ),
    'blue':
    (
        (0., 0, 0),
        (lognum[0], 0, 0),
        (lognum[1], 0, 0),
        (lognum[2], 0, 0),
        (lognum[3], 0, 0),
        (lognum[4], 0, 0),
    (lognum[5], 1, 1)
    )
}


mycmap = matplotlib.colors.LinearSegmentedColormap('my_colormap', cdict, 256)   
norm = matplotlib.colors.LogNorm(vmin, vmax)
colors = {}
count = 0
step_size = 0.001
for value in np.arange(vmin, vmax+step_size, step_size):
    count += 1
    print("%d/%d %f%%" % (count, vmax*(1./step_size), 100.*count/(vmax*(1./step_size))))
    rgba = mycmap(norm(value), bytes=True)
    color = (rgba[0], rgba[1], rgba[2])
    if color not in colors.values():
        colors[value] = color

print ("value, red, green, blue")
for value in sorted(colors.keys()):
    rgb = colors[value]
    print("%s, %s, %s, %s" % (value, rgb[0], rgb[1], rgb[2]))

0
我找到了一种很简洁的方法来返回使用Matplotlib生成的N个十六进制颜色的列表。
import matplotlib

# Choose colormap
cmap = plt.cm.get_cmap('terrain', N)
colors = [matplotlib.colors.to_hex(cmap(i)) for i in range(N)]

希望能帮到你!

0
这里有一个解决方案,它提供了一组离散的颜色值。中点可以通过将离散的颜色映射分成两半来确定。
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

# Choose a colormap from Matplotlib
colormap = plt.cm.viridis

# Set number of colors
num_colors = 5

# Create a ListedColormap with discrete colors
discrete_cmap = ListedColormap(colormap(np.linspace(0, 1, num_colors)))

# Output RGB values
for i, rgb in enumerate(discrete_cmap.colors):
    print(f"Color {i + 1}: RGB = {rgb}")

# Plot a colorbar to visualize
plt.imshow([[i] for i in range(num_colors)], cmap=discrete_cmap, aspect='auto')
plt.colorbar(ticks=range(num_colors))
plt.show()

-3

如果想要快速并且简单粗暴,可以直接使用地图。 或者你也可以按照 @amaliammr 所说的做。

data_size = 23   # range 0..23
colors = plt.cm.turbo
color_normal = colours.N/data_size

for i in range(data_size):
    col = colours.colors[int(i*color_normal)]



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