Colormap with maximum distinguishable colours

后端 未结 2 430
野性不改
野性不改 2021-02-05 15:49

Using matplotlib in Python I\'m plotting anywhere between 20 and 50 lines. Using matplotlib\'s sliding colour scales these become indistinguishable after a certain number of li

相关标签:
2条回答
  • 2021-02-05 16:11

    Recently, I also encountered the same problem. So I created the following simple Python code to generate visually distinguishable colors for jupyter notebook matplotlib. It is not quite maximally perceptually distinguishable, but it works better than most built-in colormaps in matplotlib.

    The algorithm splits the HSV scale into 2 chunks, 1st chunk with increasing RGB value, 2nd chunk with decreasing alpha so that the color can blends into the white background.

    Take note that if you are using any toolkit other than jupyter notebook, you must make sure the background is white, otherwise, alpha blending will be different and the resultant color will also be different.

    Furthermore, the distinctiveness of color highly depends on your computer screen, projector, etc. A color palette distinguishable on one screen does not necessarily imply on another. You must physically test it out if you want to use for presentation.

    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)))
    

    0 讨论(0)
  • 2021-02-05 16:14

    I loved the idea of palette created by @xuancong84 and modified his code a bit to make it not depending on alpha channel. I drop it here for others to use, thank you @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)
    
    

    These are the colours I get:

    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()
    

    0 讨论(0)
提交回复
热议问题