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 lines are plotted (well before 20).
While I've seen a few example of code in Matlab and C# to create colour maps of an arbitrary number of colours which are maximally distinguishable from one another I can't find anything for Python.
Can anyone point me in the direction of something in Python that will do this?
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 # 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()
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)))