@clairew As I understood you need a discrete colorscale derived from a continuous one.
Unlike the matplotlib, a plotly continuous colorscale is defined by a number of colors much smaller than 256 (the usual number of colors in a matplotlib colormap).
To get a binned colorscale from a plotly continuous colorscale we have to find the colors of the new
discrete colorscale from those of the continuous one, through linear interpolation, using the function np.interp.
We can get the colors of a continuous colorscale by calling:
px.colors.sequential.ColorscaleName or px.colors.diverging.ColorscaleName.
As we can notice below these colors are either hex or rgb colors, and their number differ from colorscale to colorscale.
I define below a function that display the swatches corresponding to colors returned by px.colors…
from plotly.colors import unlabel_rgb, hex_to_rgb
import plotly.graph_objects as go
import plotly.express as px
import numpy as np
def colorscale_swatches(seq):
data = [go.Bar(x=[k], y=[1], marker_color=c, hovertext=seq[k]) for k, c in enumerate(seq)]
fig = go.Figure(data=data)
fig.update_layout(bargap=0.02, template="none",
showlegend=False,
xaxis=dict(showticklabels=False, showgrid=False),
yaxis_showticklabels=False,
height=250)
return fig
Let us plot the swatches corresponding to Viridis:
seq1 = px.colors.sequential.Viridis
seq1
['#440154',
'#482878',
'#3e4989',
'#31688e',
'#26828e',
'#1f9e89',
'#35b779',
'#6ece58',
'#b5de2b',
'#fde725']
fig1 = colorscale_swatches(seq1)
Notice that we have 10 swatches, and their color is given in hex.
Now take the colors for the colormap cmocean.matter
seq2 = px.colors.sequential.matter
seq2
['rgb(253, 237, 176)',
'rgb(250, 205, 145)',
'rgb(246, 173, 119)',
'rgb(240, 142, 98)',
'rgb(231, 109, 84)',
'rgb(216, 80, 83)',
'rgb(195, 56, 90)',
'rgb(168, 40, 96)',
'rgb(138, 29, 99)',
'rgb(107, 24, 93)',
'rgb(76, 21, 80)',
'rgb(47, 15, 61)']
In this case there are 12 rgb colors, and their swatches are:
fig2 = colorscale_swatches(seq2)
But in our projects we need a discrete colorscale derived from a continuous one containing different number of colors.
The following function does the job, i.e. from a given sequence of colors derived from a continuous colorscale it returns a discrete colorscale of nr_swatches colors.
def binned_colorscale(seq, nr_swatches=5):
"""
seq: a sequence of hex colors or rgb colors returned by px.colors.sequential.ColorscaleName
or px.colors.diverging.ColorscaleName
nr_swatches: the number of swatches for the discrete colorscale, derived from ColorscaleName
"""
if seq[0][0] == '#':
arr_colors=np.array([hex_to_rgb(s) for s in seq])/255
elif seq[0][0:3] == 'rgb':
arr_colors = np.array([unlabel_rgb(s) for s in seq])/255
else:
raise ValueError("a plotly colorscale is given either with hex colors or as rgb colors")
n = len(seq)
svals = [k/(n-1) for k in range(n)] #the scale values corresponding to the colors in seq
grid = [k/(nr_swatches-1) for k in range(nr_swatches)]# define the scale values corresponding nr_swatches
r, g, b = [np.interp(grid, svals, arr_colors[:, k]) for k in range(3)] #np.interp interpolates linearly
cmap_arr = np.clip(np.vstack((r, g, b)).T, 0, 1)
new_colors = np.array(cmap_arr*255, dtype=int)
discrete_colorscale = []
N = len(new_colors+1)
for k in range(N):
discrete_colorscale.extend([[k/N, f'rgb{tuple(new_colors[k])}'],
[(k+1)/N, f'rgb{tuple(new_colors[k])}']])
return discrete_colorscale
example:
dcolorsc = binned_colorscale(seq2, nr_swatches=7)
[[0.0, 'rgb(253, 237, 176)'],
[0.14285714285714285, 'rgb(253, 237, 176)'],
[0.14285714285714285, 'rgb(246, 178, 123)'],
[0.2857142857142857, 'rgb(246, 178, 123)'],
[0.2857142857142857, 'rgb(234, 120, 88)'],
[0.42857142857142855, 'rgb(234, 120, 88)'],
[0.42857142857142855, 'rgb(205, 67, 86)'],
[0.5714285714285714, 'rgb(205, 67, 86)'],
[0.5714285714285714, 'rgb(158, 36, 97)'],
[0.7142857142857143, 'rgb(158, 36, 97)'],
[0.7142857142857143, 'rgb(101, 23, 90)'],
[0.8571428571428571, 'rgb(101, 23, 90)'],
[0.8571428571428571, 'rgb(47, 15, 61)'],
[1.0, 'rgb(47, 15, 61)']]
I hope that this is what you required. The colorbar associated to such a colorscale is also binned. Now to use such a discrete colorscale of `nr_swatches’ colors your data values should be binned as follows:
boundary_vals=[minval+k*(maxval-minval)/nr_swatches for k in range(nr_swatches+1]