具有最大可区分颜色的色彩地图

20

在Python中使用matplotlib绘制20到50条线。使用matplotlib的滑动颜色比例,在绘制一定数量的线之后,这些线会变得难以区分(在20条之前)。

虽然我看过一些Matlab和C#代码示例,用于创建可以互相区分的任意数量颜色的颜色映射,但我找不到Python的相关内容。

有人可以指引我如何在Python中实现这个功能吗?

谢谢!


1
这个链接可能会有所帮助。 - ImportanceOfBeingErnest
2
太好了,谢谢。我只需复制颜色列表并根据需要访问它们。 - WRJ
1
这个工具将允许您创建自己的颜色列表。同样,还有这个。这个想法始终是一样的:从许多颜色列表中开始,根据需要选择一个N项子集。这个链接为:http://tools.medialab.sciences-po.fr/iwanthue/ - ImportanceOfBeingErnest
谢谢,我也会尝试一下! - WRJ
显示剩余2条评论
2个回答

14

我喜欢 @xuancong84 创建的调色板概念,并对他的代码进行了一些修改,使其不再依赖于 alpha 通道。我在这里分享给其他人使用,感谢 @xuancong84!

import math

import numpy as np
from matplotlib.colors import ListedColormap
from matplotlib.cm import hsv


def generate_colormap(number_of_distinct_colors: int = 80):
    if number_of_distinct_colors == 0:
        number_of_distinct_colors = 80

    number_of_shades = 7
    number_of_distinct_colors_with_multiply_of_shades = int(math.ceil(number_of_distinct_colors / number_of_shades) * number_of_shades)

    # Create an array with uniformly drawn floats taken from <0, 1) partition
    linearly_distributed_nums = np.arange(number_of_distinct_colors_with_multiply_of_shades) / number_of_distinct_colors_with_multiply_of_shades

    # We are going to reorganise monotonically growing numbers in such way that there will be single array with saw-like pattern
    #     but each saw tooth is slightly higher than the one before
    # First divide linearly_distributed_nums into number_of_shades sub-arrays containing linearly distributed numbers
    arr_by_shade_rows = linearly_distributed_nums.reshape(number_of_shades, number_of_distinct_colors_with_multiply_of_shades // number_of_shades)

    # Transpose the above matrix (columns become rows) - as a result each row contains saw tooth with values slightly higher than row above
    arr_by_shade_columns = arr_by_shade_rows.T

    # Keep number of saw teeth for later
    number_of_partitions = arr_by_shade_columns.shape[0]

    # Flatten the above matrix - join each row into single array
    nums_distributed_like_rising_saw = arr_by_shade_columns.reshape(-1)

    # HSV colour map is cyclic (https://matplotlib.org/tutorials/colors/colormaps.html#cyclic), we'll use this property
    initial_cm = hsv(nums_distributed_like_rising_saw)

    lower_partitions_half = number_of_partitions // 2
    upper_partitions_half = number_of_partitions - lower_partitions_half

    # Modify lower half in such way that colours towards beginning of partition are darker
    # First colours are affected more, colours closer to the middle are affected less
    lower_half = lower_partitions_half * number_of_shades
    for i in range(3):
        initial_cm[0:lower_half, i] *= np.arange(0.2, 1, 0.8/lower_half)

    # Modify second half in such way that colours towards end of partition are less intense and brighter
    # Colours closer to the middle are affected less, colours closer to the end are affected more
    for i in range(3):
        for j in range(upper_partitions_half):
            modifier = np.ones(number_of_shades) - initial_cm[lower_half + j * number_of_shades: lower_half + (j + 1) * number_of_shades, i]
            modifier = j * modifier / upper_partitions_half
            initial_cm[lower_half + j * number_of_shades: lower_half + (j + 1) * number_of_shades, i] += modifier

    return ListedColormap(initial_cm)

这是我得到的颜色:

from matplotlib import pyplot as plt
import numpy as np

N = 16
M = 7
H = np.arange(N*M).reshape([N,M])
fig = plt.figure(figsize=(10, 10))
ax = plt.pcolor(H, cmap=generate_colormap(N*M))
plt.show()

输入图像描述


1
太棒了,这正是我需要的甘特图,有大约50个任务。谢谢! - Grumpybeard

7

最近,我也遇到了同样的问题。因此,我创建了以下简单的Python代码,为jupyter notebook matplotlib生成视觉上可区分的颜色。它不是完全最大程度地感知区别,但它比matplotlib内置的大多数colormap要好。

该算法将HSV比例尺分成两个部分:第一个部分具有增加的RGB值,第二个部分具有减小的alpha值,以便颜色可以混合到白色背景中。

请注意,如果您使用的是jupyter notebook之外的任何工具包,则必须确保背景是白色,否则alpha混合将不同,结果颜色也将不同。

此外,颜色的区别性高度取决于您的计算机屏幕、投影仪等。在一个屏幕上可区分的颜色调色板未必在另一个屏幕上也可区分。如果您想用于演示,您必须进行物理测试。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

def generate_colormap(N):
    arr = np.arange(N)/N
    N_up = int(math.ceil(N/7)*7)
    arr.resize(N_up)
    arr = arr.reshape(7,N_up//7).T.reshape(-1)
    ret = matplotlib.cm.hsv(arr)
    n = ret[:,3].size
    a = n//2
    b = n-a
    for i in range(3):
        ret[0:n//2,i] *= np.arange(0.2,1,0.8/a)
    ret[n//2:,3] *= np.arange(1,0.1,-0.9/b)
#     print(ret)
    return ret

N = 16
H = np.arange(N*N).reshape([N,N])
fig = plt.figure(figsize=(10, 10))
ax = plt.pcolor(H, cmap=ListedColormap(generate_colormap(N*N)))

enter image description here


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