将字符串与随机颜色 matplotlib 匹配的优雅方式

Posted

技术标签:

【中文标题】将字符串与随机颜色 matplotlib 匹配的优雅方式【英文标题】:Elegant way to match a string to a random color matplotlib 【发布时间】:2015-10-31 05:10:03 【问题描述】:

我想将一些数据的标签转换为颜色,以便用 matplotlib 绘图

我有一个名单["bob", "joe", "andrew", "pete"]

在 matplotlib 中是否有一种内置方法可以将这些字符串与颜色值进行映射?我考虑过随机创建十六进制值,但最终可能会得到相似的颜色或不可见的颜色。

我尝试了几种不同的方法来尝试从以下 cmap 答案中创建键值:

这个:

#names is a list of distinct names
cmap = plt.get_cmap('cool')
colors = cmap(np.linspace(0, 1, len(names)))
clr = names[i]: colors[i] for i in range(len(names))
ax.scatter(x, y, z, c=clr)

【问题讨论】:

另见***.com/questions/26139423/… 和github.com/matplotlib/matplotlib/issues/6214 【参考方案1】:

选择一个color map,比如viridis

cmap = plt.get_cmap('viridis')

颜色图cmap 是一个函数,它可以获取从 0 到 1 的值数组并将它们映射到 RGBA 颜色。 np.linspace(0, 1, len(names)) 生成长度为 len(names) 的从 0 到 1 的等距数字数组。因此,

colors = cmap(np.linspace(0, 1, len(names)))

viridis 颜色映射中选择等距颜色。

注意这里不是使用字符串的,它只是使用列表中字符串的序号位置来选择颜色。另请注意,这些不是随机颜色,这只是从任意字符串列表生成唯一颜色的简单方法。


所以:

import numpy as np
import matplotlib.pyplot as plt

cmap = plt.get_cmap('viridis')
names = ["bob", "joe", "andrew", "pete"]
colors = cmap(np.linspace(0, 1, len(names)))
print(colors)
# [[ 0.267004  0.004874  0.329415  1.      ]
#  [ 0.190631  0.407061  0.556089  1.      ]
#  [ 0.20803   0.718701  0.472873  1.      ]
#  [ 0.993248  0.906157  0.143936  1.      ]]

x = np.linspace(0, np.pi*2, 100)
for i, (name, color) in enumerate(zip(names, colors), 1):
    plt.plot(x, np.sin(x)/i, label=name, c=color)
plt.legend()
plt.show()


问题

clr = names[i]: colors[i] for i in range(len(names))
ax.scatter(x, y, z, c=clr)

ax.scatterc 参数期望RGB(A) 的sequence 与x 相同长度的值或单一颜色。 clr 是一个字典,而不是一个序列。所以 如果colors 的长度与x 相同,那么您可以使用

ax.scatter(x, y, z, c=colors)

【讨论】:

我试图将这些作为 3d scatter 中的 c 参数输入我试过 zip(names, colors) #names 是名称列表,颜色是 cmap#,我在 matplotlib 中遇到错误。有没有办法解决这个问题 你能发布代码和完整的回溯错误信息吗? 我通过把它变成字典来添加我试图做的事情 ax.scatterc 参数需要 RGB(A) 值的序列(例如列表或数组),它不接受字典。因此,如果您想为x,y,z 的每个值使用不同的颜色,请使用c=colors 而不是c=clr【参考方案2】:

我使用哈希函数来获取 0 到 1 之间的数字,即使你不知道所有标签也可以使用它:

x = [1, 2, 3, 4, 5]
labels = ["a", "a", "b", "b", "a"]
y = [1, 2, 3, 4, 5]

colors = [float(hash(s) % 256) / 256 for s in labels]      

plt.scatter(x, y, c=colors, cmap="jet")
plt.show()

【讨论】:

这个答案对我解决问题很有启发。但它可能有问题的一件事是,hash(s) % 256 对于labels 中的两个不同字符串可能是相同的。【参考方案3】:

这让我非常不安,我写了get_cmap_string,它返回一个与cmap 完全相同但也作用于字符串的函数。

data = ["bob", "joe", "pete", "andrew", "pete"]
cmap = get_cmap_string(palette='viridis', domain=data)
cmap("joe")
# (0.20803, 0.718701, 0.472873, 1.0)
cmap("joe", alpha=0.5)
# (0.20803, 0.718701, 0.472873, 0.5)

1。实施

所有其他答案提到的基本思想是我们需要一个哈希表——从我们的字符串到整数的映射,它是唯一的。在 python 中,这只是一个字典。

hash(str) 不起作用的原因是,即使 matplotlib 的 cmap 接受任何整数,两个不同的字符串也有可能获得相同的颜色。例如,如果hash(str1)8 并且hash(str2)18,我们将cmap 初始化为get_cmap(name=palette, lut=10) 那么cmap(hash(str1)) 将与cmap(hash(str2)) 相同

代码

import numpy as np
import matplotlib.cm
def get_cmap_string(palette, domain):
    domain_unique = np.unique(domain)
    hash_table = key: i_str for i_str, key in enumerate(domain_unique)
    mpl_cmap = matplotlib.cm.get_cmap(palette, lut=len(domain_unique))

    def cmap_out(X, **kwargs):
        return mpl_cmap(hash_table[X], **kwargs)

    return cmap_out

2。用法

与其他答案中的示例相同,但现在请注意名称 pete 出现了两次。

import matplotlib.pyplot as plt

# data
names = ["bob", "joe", "pete", "andrew", "pete"]

# color map for the data
cmap = get_cmap_string(palette='viridis', domain=names)

# example usage
x = np.linspace(0, np.pi*2, 100)
for i_name, name in enumerate(names):
    plt.plot(x, np.sin(x)/i_name, label=name, c=cmap(name))
plt.legend()
plt.show()

您可以看到,图例中的条目是重复的。解决这个问题是另一个挑战,请参阅here。 或者按照here 的说明使用自定义图例。

3。替代品

就 matplotlib 开发人员的讨论而言,他们建议使用 Seaborn。 请参阅讨论 here 和示例用法 here。

【讨论】:

【参考方案4】:

这是另一个选择:

names = ["bob", "joe", "andrew", "pete"]

colmap = name: n for n, name in enumerate(set(names)) # <-- uses 'set' to get unique names from list

ax.scatter(
    x, y, z, 
    c = [colmap[name] for name in names],
    cmap = 'tab10' # <-- not necessary but helpful if you want to make sure colors aren't similar
)

它只是将名称转换为整数,matplotlib 自动决定如何将整数转换为颜色。如果您使用qualitative colormap,则可以确保颜色不会相似,例如tab10

【讨论】:

以上是关于将字符串与随机颜色 matplotlib 匹配的优雅方式的主要内容,如果未能解决你的问题,请参考以下文章

简单的Python项目——可视化数据入门(关键词:matplotlib,pyolot,pygal)

在matplotlib中使颜色变暗或变亮

MNIST Matplotlib:显示颜色

Matplotlib使用与系列对应的颜色创建文本

python数据可视化--matplotlib进行随机漫步

使用匹配替换随机字符串/字符数组字符