Search code examples
pythonformattingplotlyheatmapcolorbar

Tickval does not assign correct position to ticktext


I am plotting an annotated heatmap with a colour bar using the code below, inspired in part by this very helpful tutorial (its part of a larger bit of code which I'll put at the end).

final_df is a dataframe whose columns are protein IDs and index is features of interest (only one in this example - M100867). A non-0 int in a heatmap cell means the protein associated with the cell is part of the feature associated with the row/index, and each non-0 int (between 1 and 6) maps to a protein grouping that I'd like to use to color my cells.

#print (final_df) 
#         C8E97_RS35225  C8E97_RS12075  ...  C8E97_RS12225  C8E97_RS12230
#M100867              0              0  ...              0              0
#
#[1 rows x 31 columns]

I'm plotting the heatmap with the code block below:

fig_df = ff.create_annotated_heatmap(final_df.values.tolist(), 
                                         x= list(final_df.columns), 
                                         y=list(final_df.index), 
                                         annotation_text  = cell_labels, #a 1x31 nested list of empty strings to remove cell annotations
                                         colorscale=dcolorsc,
                                         colorbar = dict(thickness=25, 
                                                         tickvals=tickvals, 
                                                         ticktext=ticktext,
                                                         tickmode = 'array'),
                                         showscale  = True,
                                         xgap = 10,
                                         ygap = 10)

Unfortunately, my colorbar ticktexts dont match up with tickvals, and I have no idea why - it should be one label ('biosynthetic', etc) per colored block in the colorbar on the right of the image:

sample output

Any pointers?

Cheers!

Tim

Full code:

def discrete_colorscale(bvals, colors):
        #https://chart-studio.plotly.com/~empet/15229/heatmap-with-a-discrete-colorscale/#/
        """
        bvals - list of values bounding intervals/ranges of interest
        colors - list of rgb or hex colorcodes for values in [bvals[k], bvals[k+1]],0<=k < len(bvals)-1
        returns the plotly  discrete colorscale
        """
        if len(bvals) != len(colors)+1:
            raise ValueError('len(boundary values) should be equal to  len(colors)+1')
        bvals = sorted(bvals)     
        nvals = [(v-bvals[0])/(bvals[-1]-bvals[0]) for v in bvals]  #normalized values
        
    dcolorscale = [] #discrete colorscale
    for k in range(len(colors)):
        dcolorscale.extend([[nvals[k], colors[k]], [nvals[k+1], colors[k]]])
    return dcolorscale


bvals = [0,1,2,3,4,5,6,7]

colors_map = ['rgb(255,255,255)', #white
              'rgb(255,0,0)', #red
              'rgb(255, 128, 0)', #orange
              'rgb(0, 0, 255)', #blue
              'rgb(128, 128, 128)', #grey
              'rgb(0, 255, 0)', #green
              'rgb(192, 192, 192)'] #light grey

dcolorsc = discrete_colorscale(bvals, colors_map)
#[[0.0, 'rgb(255,255,255)'], 
# [0.14285714285714285, 'rgb(255,255,255)'], 
# [0.14285714285714285, 'rgb(255,0,0)'], 
# [0.2857142857142857, 'rgb(255,0,0)'], 
# [0.2857142857142857, 'rgb(255, 128, 0)'], 
# [0.42857142857142855, 'rgb(255, 128, 0)'], 
# [0.42857142857142855, 'rgb(0, 0, 255)'], 
# [0.5714285714285714, 'rgb(0, 0, 255)'], 
# [0.5714285714285714, 'rgb(128, 128, 128)'], 
# [0.7142857142857143, 'rgb(128, 128, 128)'], 
# [0.7142857142857143, 'rgb(0, 255, 0)'], 
# [0.8571428571428571, 'rgb(0, 255, 0)'], 
# [0.8571428571428571, 'rgb(192, 192, 192)'], 
# [1.0, 'rgb(192, 192, 192)']]

bvals = np.array(bvals)
tickvals = [np.mean(bvals[k:k+2]) for k in range(len(bvals)-1)]
ticktext  = ['not in module', 
             'biosynthetic',
             'biosynthetic-additional',
             'other',
             'regulatory',
             'resistance',
             'transport']  

fig_df = ff.create_annotated_heatmap(final_df.values.tolist(), 
                                     x= list(final_df.columns), 
                                     y=list(final_df.index), 
                                     annotation_text  = cell_labels, #a 1x31 nested list of empty strings to remove cell annotations
                                     colorscale=dcolorsc,
                                     colorbar = dict(thickness=25, 
                                                     tickvals=tickvals, 
                                                     ticktext=ticktext,
                                                     tickmode = 'array'),
                                     showscale  = True,
                                     ygap = 10,
                                     xgap = 10)
fig_df.update_layout(
xaxis=dict(
    
    rangeslider=dict(
        visible=True
    )
)
)
fig_df.write_html(results_file_path)

Solution

  • Fixed it - needs a specified zmin and zmax (max/min val in bvals array) to properly proportion the color bar:

    fig_df = ff.create_annotated_heatmap(final_df.values.tolist(), 
                                         x= list(final_df.columns), 
                                         y=list(final_df.index), 
                                         annotation_text  = cell_labels, 
                                         colorscale=dcolorsc,
                                         colorbar = dict(thickness=25, 
                                                         tickvals=tickvals, 
                                                         ticktext=ticktext),
                                         showscale  = True,
                                         zmin=0, ###
                                         zmax=7, ###
                                         ygap = 10,
                                         xgap = 10)