# Discrete colorbar with PlotlyJS

Hi, wonder if there is any good way to make a discrete colour bar when making a heatmap plot with PlotlyJS? It would also be nice to be able to label these.

Here is an example:

The code to produce this is

``````
using PlotlyJS

## Set-up:
# Define the categories and corresponding border levels between them:
categories = ["Undetectable", "Low", "Medium", "High", "Extreme", "Wat"]
levels = [0, 8, 16, 24, 32, 40, 48]
label_levels = levels .+ 4 # note the shift to centre the labels

# Some random data:
data = [
29  16  15   7  14
10   4  36  14   2
25  23   0  29  41
]
# data = rand(minimum(levels):maximum(levels), 3, 5)

# A discrete colour scheme: see https://juliagraphics.github.io/Colors.jl/dev/namedcolors/
colour_pairs = [
"crimson"      => (220,  20,  60),
"orangered"    => (255,  69,   0),
"goldenrod"    => (218, 165,  32),
"aquamarine3"  => (102, 205, 170),
"royalblue"    => ( 65, 105, 225),
"darkviolet"   => (148,   0, 211),
"black"        => (  0,   0,   0),
]
# Make it into a suitable list:
colour_list = map(x -> "rgb\$(x)", colour_pairs)

# The tricky part - creating ordered ranges of colours:
norm_levels = (levels .- minimum(levels))/maximum(levels)
colour_marks = [[norm_levels, colour_list]]
for i in 2:length(levels)
push!(colour_marks, [norm_levels[i], colour_list[i-1]])
push!(colour_marks, [norm_levels[i], colour_list[i]])
end

## Plot:
# See https://plotly.com/julia/reference/heatmap/ for reference to keyword arguments
function heatmap2()
trace = heatmap(
x=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
y=["Morning", "Afternoon", "Evening"],
z=data,
autocolorscale=false,
colorbar=attr(tickmode="array",
tickvals=label_levels,
ticktext=categories),
colorscale=colour_marks,
zmin=minimum(levels),
zmax=maximum(levels)
)
plot(trace)
end
p = heatmap2()
``````

I post a Julia version of the plotly.py code for a heatmap with discrete colorscale
https://chart-studio.plotly.com/~empet/15229/heatmap-with-a-discrete-colorscale/#/

``````using PlotlyJS
function discrete_colorscale(boundary_vals, mycolors)
#boundary_vals - vector of values bounding intervals/ranges of interest
#colors - list of rgb or hex colorcodes for values in [bvals[k], bvals[k+1]],1<=k < length(boundary_vals)
#returns the plotly  discrete colorscale
if length(boundary_vals) != length(mycolors)+1
error("length of boundary values should be equal to  length(mycolors)+1")
end
bvals = sort(boundary_vals)
nvals = [(v-bvals)/(bvals[end]-bvals) for v in bvals]  #normalized values

dcolorscale = [] #discrete colorscale
for k in 1:length(mycolors)
append!(dcolorscale, [[nvals[k], mycolors[k]], [nvals[k+1], mycolors[k]]])
end
return dcolorscale

end

function colorbar_ticks(bvals)
tickvals = [sum(bvals[k:k+1])/2 for k in 1:length(bvals)-1] #position with respect to bvals, to be placed ticktext
ticktext = String[]
push!(ticktext, "<\$(bvals)")
for k in 2:length(bvals)-2
push!(ticktext, "\$(bvals[k])-\$(bvals[k+1])")
end
push!(ticktext, ">\$(bvals[end-1])")
return tickvals, ticktext
end

z = rand(2:90, 20, 20)
bvals = [2, 15, 40, 65, 90]
mycolors = ["#09ffff", "#19d3f3", "#e763fa" , "#ab63fa"]
#mycolors = ["rgb(133,187,77)","rgb(202,216,216)","rgb(238,164,2)", "rgb(221,114,54)"]
dcolorscale = discrete_colorscale(bvals, mycolors)

tvals, ttext= colorbar_ticks(bvals)

pl = Plot(heatmap(z=z, colorscale=dcolorscale,
colorbar=attr(tickvals=tvals, ticktext=ttext)),
Layout(width=450, height=400))
`````` 1 Like

This is really nice; more adaptive than my example by using functions to generate labels, for example. And I really like your colour choice !

Thanks for you`re advice, by using a combination of these suggestions I ended up with a really nice plot! Thanks!

1 Like