使用 matplotlib 创建超过 20 种独特的图例颜色

Posted

技术标签:

【中文标题】使用 matplotlib 创建超过 20 种独特的图例颜色【英文标题】:creating over 20 unique legend colors using matplotlib 【发布时间】:2012-01-13 10:45:51 【问题描述】:

我正在使用 matplotlib 在一个绘图上绘制 20 条不同的线。我使用 for 循环来绘制并用其键标记每一行,然后使用图例函数

for key in dict.keys():
    plot(x,dict[key], label = key)
graph.legend()

但是使用这种方式,图表会在图例中重复很多颜色。有什么方法可以确保使用 matplotlib 和超过 20 行为每一行分配唯一的颜色?

谢谢

【问题讨论】:

碰巧图例与颜色无关。无论您是否有图例,颜色都会重复。 matplotlib 默认情况下如此轻松地重复使用颜色,这让我很生气 【参考方案1】:

这些答案似乎比需要的要复杂。如果您正在遍历列表以绘制线条,则只需在列表上枚举并将颜色分配给颜色图上的某个点。假设您正在遍历 pandas 数据框中的所有列:

fig, ax = plt.subplots()
cm = plt.get_cmap('gist_rainbow')
 for count, col in enumerate(df.columns):
    ax.plot(df[col], label = col, linewidth = 2, color = cm(count*20))

这是可行的,因为 cm 只是一个可迭代的颜色数字字典。将它们乘以某个因子可以让您在颜色图中走得更远(颜色差异更大)。

【讨论】:

什么是 ColList?还有为什么不在 Python 中使用 snail_case? 我编辑了我的评论 - ColList 是熊猫数据框中的列列表。 df.columns 会更清楚。我正在使用熊猫,但您可以迭代任何您想要的数据。我对 snail_case 不熟悉。 非常简单,谢谢【参考方案2】:

您的问题的答案与另外两个 SO 问题有关。

How to pick a new color for each plotted line within a figure in matplotlib? 的答案解释了如何定义默认的颜色列表,该列表循环选择下一个要绘制的颜色。这是通过Axes.set_color_cycle method 完成的。

不过,您希望获得正确的颜色列表,这可以使用颜色图轻松完成,正如该问题的答案所述:Create a color generator from given colormap in matplotlib。有一个颜色映射表取一个从 0 到 1 的值并返回一个颜色。

因此,对于您的 20 行,您希望以 1/20 的步长从 0 循环到 1。具体来说,您希望从 0 循环到 19/20,因为 1 映射回 0。

这是在这个例子中完成的:

import matplotlib.pyplot as plt
import numpy as np

NUM_COLORS = 20

cm = plt.get_cmap('gist_rainbow')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_color_cycle([cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
for i in range(NUM_COLORS):
    ax.plot(np.arange(10)*(i+1))

fig.savefig('moreColors.png')
plt.show()

这是结果图:

替代的、更好的(有争议的)解决方案

还有一种替代方法是使用ScalarMappable 对象将一系列值转换为颜色。这种方法的优点是您可以使用非线性Normalization 将线索引转换为实际颜色。以下代码产生相同的结果:

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

NUM_COLORS = 20

cm = plt.get_cmap('gist_rainbow')
cNorm  = colors.Normalize(vmin=0, vmax=NUM_COLORS-1)
scalarMap = mplcm.ScalarMappable(norm=cNorm, cmap=cm)
fig = plt.figure()
ax = fig.add_subplot(111)
# old way:
#ax.set_color_cycle([cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
# new way:
ax.set_color_cycle([scalarMap.to_rgba(i) for i in range(NUM_COLORS)])
for i in range(NUM_COLORS):
    ax.plot(np.arange(10)*(i+1))

fig.savefig('moreColors.png')
plt.show()

弃用说明 在 mplib (1.5+) 的更新版本中,set_color_cycle 函数已被弃用,取而代之的是 ax.set_prop_cycle(color=[...])

【讨论】:

不错。顺便说一句,'color' 在你的 for 循环中做了什么?我在循环中删除了它的声明,代码似乎运行良好...... ax.set_color_map() 在 matplotlib v1.5 中已弃用。请改用ax.set_prop_cycle(color=[cm...]) 可用颜色图列表在这里:matplotlib.org/examples/color/colormaps_reference.html 我在将这个配色方案应用到我自己在此处发布的代码中遇到了很多麻烦:***.com/questions/47775914/…。它被认为是这篇文章的副本,这是正确的,但我无法让这里的答案对我有用。 这是我在 *** 上见过的最吸引人的答案之一【参考方案3】:

以Don Kirkby's answer 为基础,如果您愿意安装/使用seaborn,那么您可以为您计算颜色:

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

NUM_COLORS = 20
LINE_STYLES = ['solid', 'dashed', 'dashdot', 'dotted']
NUM_STYLES = len(LINE_STYLES)

sns.reset_orig()  # get default matplotlib styles back
clrs = sns.color_palette('husl', n_colors=NUM_COLORS)  # a list of RGB tuples
fig, ax = plt.subplots(1)
for i in range(NUM_COLORS):
    lines = ax.plot(np.arange(10)*(i+1))
    lines[0].set_color(clrs[i])
    lines[0].set_linestyle(LINE_STYLES[i%NUM_STYLES])

fig.savefig('moreColors.png')
plt.show()

除了能够使用 seaborn 的各种调色板之外,您还可以获得一个 RGB 元组列表,以后可以根据需要使用/操作。显然,您可以使用 matplotlib 的颜色图计算类似的东西,但我发现这很方便。

【讨论】:

谢谢!对于每个想要独特地采样颜色和线条样式的人:clrs = sns.color_palette('muted', n_colors=num_colors) product(['solid', 'dashed', 'dashdot', 'dotted'], clrs)【参考方案4】:

我有一个 12 行的图,当我尝试Yann's technique 时,我发现很难区分颜色相似的行。我的线条也是成对出现的,所以我对每对中的两条线使用了相同的颜色,并使用了两种不同的线宽。您还可以改变线条样式以获得更多组合。

你可以使用set_prop_cycle(),但我只是在调用plot()之后修改了行对象。

这是 Yann 的示例,其中包含三种不同的线宽:

import matplotlib.pyplot as plt
import numpy as np

NUM_COLORS = 20

cm = plt.get_cmap('gist_rainbow')
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(NUM_COLORS):
    lines = ax.plot(np.arange(10)*(i+1))
    lines[0].set_color(cm(i//3*3.0/NUM_COLORS))
    lines[0].set_linewidth(i%3 + 1)

fig.savefig('moreColors.png')
plt.show()

这是具有不同线条样式的相同示例。当然,如果你愿意,你可以将两者结合起来。

import matplotlib.pyplot as plt
import numpy as np

NUM_COLORS = 20
LINE_STYLES = ['solid', 'dashed', 'dashdot', 'dotted']
NUM_STYLES = len(LINE_STYLES)

cm = plt.get_cmap('gist_rainbow')
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(NUM_COLORS):
    lines = ax.plot(np.arange(10)*(i+1))
    lines[0].set_color(cm(i//NUM_STYLES*float(NUM_STYLES)/NUM_COLORS))
    lines[0].set_linestyle(LINE_STYLES[i%NUM_STYLES])

fig.savefig('moreColors.png')
plt.show()

【讨论】:

为每种颜色循环使用不同的线条样式(虚线、点线、双虚线、点划线……)不是更好吗?如果您需要引用标题中的线条,您将很难使用线条宽度(“中等粗橙色线”?)。但是,OTOH,所要求的具有 20 种不同颜色的解决方案也是如此。 当然,@NichtJens,这就是我提到线条样式作为替代方案的原因。线宽是我最先想到的,仅此而已。 明白。我的主要意思是您可能希望将其作为第二个示例添加到您的答案中以使其更完整:) 按照您的建议,我添加了第二个示例 @NichtJens。 非常有帮助的答案。还可以帮助我解决我不久前通过名称处理颜色的问题(graphicdesign.stackexchange.com/questions/84320/…)。更容易参考红色点划线与红色实线,而不是鲑鱼红线与熔岩红线(当然忽略整个色盲问题......)

以上是关于使用 matplotlib 创建超过 20 种独特的图例颜色的主要内容,如果未能解决你的问题,请参考以下文章

Matplotlib仅使用特定颜色保存图像

我如何使用教义来创建独特的关系?

警告过多的开放数字

使用每个独特团队获胜的顺序计数器创建列表

使用 seaborn/matplotlib 创建具有轴限制的图形 [重复]

深入浅出matplotlib(106):使用巴特沃斯滤波器进行带通滤波和带阻滤波