Colors for discrete ranges in heatmaps

I’m trying to make a heatmap using different colors for discreet ranges of data, using Plotly in python/pandas. My colleague sent me an image of the color ranges she would like used for the data.


I used the “Custom Discretized Heatmap Colorscale” example at https://plot.ly/python/colorscales/ but rather than discrete bars, the heatmap has a range of colors.

Here is the script I am using:
def makePlotlyHeatmap(input_df, output_file):
‘’‘make a Plotly heatmap from a dataframe’’’

    # initialize for offline use
    offline.init_notebook_mode(connected=True)

    
    # set layout options
    layout = go.Layout(
    autosize=True,
    height=1200,
    width=1200,
    margin=go.Margin(
        l=500,
        r=200,
        b=100,
        t=100,
        pad=4
    )) 
    
    # create heatmap file
    heatmap = input_df.iplot(kind='heatmap',
        layout=layout,
        colors=[
            # Let 0-10.0 fold change be white:
            [10.0, 'cmyk(0,0,0,0)'],
            # Let 10.0-50.0 fold change be orange:
            [10.0, 'cmyk(0,61,100,0)'],
            [50.0, 'cmyk(0,61,100,0)'],
            # Let 50.0-100.0 fold change be red:
            [50.0, 'cmyk(88,77,0,0)'],
            [100.0, 'cmyk(88,77,0,0)'],
            # Let 100.0 and greater fold change be blue:
            [100, 'cmyk(24,100,100,22)']],
        asFigure=True)

    
    # write heatmap to file
    offline.plot(heatmap, filename=output_file, auto_open=True)

makePlotlyHeatmap(df_data, "output.html")

The data frame (df_data) contains a read-in csv.

I have tried “colorscale” in place of “colors” but that gives the error “AttributeError: ‘list’ object has no attribute ‘lower’”.

Thanks for your assistance!

@heff The heatmap trace selects the color from a colorscale, according to the normalized values in the lists of lists z (or a numpy array of shape (m,n)).

The discrete colorscale should be defined as follows:

  • find the max value for z, let us say that it is 125:

  • map the interval [0, max_val] to [0,1] by t--> t/max_val

  • mapping your thresholds [0, 10, 50, 100, 125] to [0,1], you get
    [0, 0.08, 0.4, 0.8, 1].

  • define the discrete colorscale:

    my_colorsc=[[0, 'rgb(255,255,255)'],#white
                [0.08, 'rgb(255,255,255'], 
                [0.08, 'rgb(255,165,0)'],#orange
                [0.4, 'rgb(255,165,0)'],
                [0.4, 'rgb(255,0,0'], #red
                [0.8, 'rgb(255,0,0)'],
                [0.8, 'rgb(0,0,255)'], #blue
                [1, 'rgb(0,0,255)']]
    

Plotly doesn’t recognize cmyk colors.

@empet - thanks for this, it is a very handy guide. Do you have any inclination if an “absolute” colorscale is possible? My use case is that I’m reading data in from a DB, and showing on a scatter with a discrete colorscale based off a value in the DB. The rub is that I’m querying from the DB and limiting to one client each time. Each client has a different range of values to color based on (one might be from 30-70, another might be from 45-55, 16-62, etc… you get the idea). I’d like the colorscales to take to the absolute range like @heff was originaly trying to code it.

I welcome any thoughts here…thx

@jezlax The normalization of range ends, above, was necessary to define the discrete colorscale. The original values are also normalized by plotly.js in order to map them to the coresponding color.

You only have to insert a suitable text as a colorbar ticktext (see the image below).

I detailed all ingredients needed to plot a heatmap with a discrete colorscale in this notebook https://plot.ly/~empet/15229.

thanks @emp for the work. the only thing i’m running into is that plotly still normalized my Z inputs, when I want them to be treated absolutely. I provided an example here: https://plot.ly/~coryjez/3

Basically the scale is from 0 to 100, and I feed in say values between 34 and 60. It normalizes those values I feed in to be 0-100 now and plots them as such. Basically, I’ve created a percentile score for my client on the backend, and just want to place that percentile score on the range I’ve provided in that notebook. It’s seeming like this won’t be possible in plotly’s current manifestation of this, but wanted to see what you thought. thanks for the help.

@jezlax, you can force the inputs to be treated absolutely by setting a zmin and zmax value in your heatmap.

heatmap = go.Heatmap(z=z, 
                     colorscale = dcolorsc,
                     zmin=0,
                     zmax=100,
                     colorbar = dict(thickness=25, 
                                     tickvals=tickvals, 
                                     ticktext=ticktext))

@michaelbabyn - right, those are super helpful! the rub i really run into here is that I can’t use those arguments in a go.Scatter() scenario, trying to plot some points and color by the scale I referenced in my post.

@jezlax, In that case you would use cmax and cmin like in this example https://plot.ly/python/colorscales/#colorscale-for-scatter-plots

Edited to fix example link.

@jezlax

I really don’t understand your complaint. Why did you say that data with range in some subinterval ([34, 60]) of the interval [min(bvals), max(bvals)] (within the function discrete_colorscale() body
bvals[0] is the min value, and bvals[-1] is the max value, after sorting) is normalized with respect to that subinterval? It is normalized with respect to
min and max bvals. If you set some cmin, cmax values in a go.Scatter instance that are different from min(bvals), max(bvals), the associated color can be wrong
because the discrete colorscale was defined such that to map the interval [min (bvals), max(bvals)] to [0,1], [not cmin, cmax].

Running your notebook (with Python 3) and hovering some points in the heatmap led to right displayed values as you can see in these images.

@empet - not meant to be a complaint at all. i didn’t realize that cmin/cmax arguments were available in go.Scatter() the same way that zmin/zmax are in go.Heatmap(). It looks like this solution scales perfectly, as you mentioned. Without the cmin/cmax being set equal to the range, in my case [0,100]… it did normalize on the subrange, but setting those arguments to 0, 100, respectively seems to work. appreciate the back and forth and the help.

1 Like
three reasons why you should buy plotly pro: support open source, get great support, host your plots and dashboards online