【问题标题】:Colormap with maximum distinguishable colours具有最大可区分颜色的颜色图
【发布时间】:2017-07-30 14:11:06
【问题描述】:

在 Python 中使用 matplotlib 我正在绘制 20 到 50 行之间的任何位置。使用 matplotlib 的滑动色标,在绘制一定数量的线后(远在 20 条之前),这些变得无法区分。

虽然我在 Matlab 和 C# 中看到了一些代码示例,用于创建任意数量颜色的颜色映射,这些颜色映射可以最大程度地相互区分,但我找不到任何 Python 代码。

谁能指出我在 Python 中可以做到这一点的方向?

干杯

【问题讨论】:

  • 这个答案似乎是关于避免索引中的颜色循环。我知道如何使用颜色图来避免这种情况,但想通过算法生成一组实际上可以通过肉眼轻松区分的颜色。一旦使用 hsv 颜色图在绘图中获得 20 条线,您最终会得到 5 种绿色,它们非常相似以至于不可能。
  • This one 可能会有所帮助。
  • 太好了,谢谢。我将复制颜色列表并根据需要访问它们。
  • This tool 将允许您创建自己的列表。同样this one 也是如此。这个想法总是一样的:从一个包含许多颜色的列表开始,然后根据需要从中选择一个N 项目子集。

标签: python matplotlib colors colormap


【解决方案1】:

我喜欢 @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()

【讨论】:

  • 太棒了,这正是我需要约 50 个任务的甘特图。谢谢!
【解决方案2】:

最近,我也遇到了同样的问题。所以我创建了以下简单的 Python 代码来为 jupyter notebook matplotlib 生成视觉上可区分的颜色。它不是最大程度地感知可区分的,但它比 matplotlib 中的大多数内置颜色图效果更好。

该算法将 HSV 比例分成 2 块,第 1 块具有增加的 RGB 值,第 2 块具有减小的 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)))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    相关资源
    最近更新 更多