Announcing Dash Bio 1.0.0 🎉 : a one-stop-shop for bioinformatics and drug development visualizations.

Slow heat map rendering of small dataframe

Hello,

I’m rendering a 212 row x 64 column DF of ints (final_df) ranging from 0 to 6 as an (annotation-less) plotly annotated heatmap. I’m doin this in my browser (microsoft edge) using a file from fig.write_html(). The final heatmap renders very slowly in my browser, to the extent I’m getting ‘page not responding’ warnings, and any zooming in/out of the graph is also very slow. This is surprising given the df is not that big.

Can anyone suggest why this is and how to speed it up?

Thanks,
Tim

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


#final_df is a 212 row x 64 col df of ints ranging from 0 to 6
#cell_df is an empty 212x64 df of empty strings to remove cell labelling behaviour
cell_df = final_df.applymap(lambda x: annot_map.get(x, x)) 
cell_labels = cell_df.values.tolist()
bvals = [0,1,2,3,4,5,6,7]

colors_map = ['rgb(244,244,255)', #whiteish 
              'rgb(255, 128, 0)', #orange 
              'rgb(255,0,0)', #red 
              '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)
bvals = np.array(bvals)
tickvals = [np.mean(bvals[k:k+2]) for k in range(len(bvals)-1)]
ticktext  = ['param 1', 
             'param 2',
             'param 3',
             'param 4',
             'param 5',
             'param 6',
             'param 7']

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 = 1,
                                      xgap = 1,
                                      )
fig_df.update_layout(
    xaxis={'title' : 'ID 1'},
    yaxis = {'title' : 'ID 2'},
    yaxis_nticks = len(final_df.index),
    xaxis_nticks = len(final_df.columns)
    )

fig_df.write_html(results_file_df)

@TimothyK

Recently has been released Plotly v5.5.0, that provides the possibility to plot annotated (and without annotations) heatmaps, calling px.imshow with text_auto='False' (the default value) for non-annotated heatmaps.
I generated synthetic data to get a DataFrame of 212 rows and 64 columns, and the final plot is opened instantly by both Chrome and Edge.

here is the code:

import pandas as pd
import numpy as np
import plotly.express as px

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
    """
    #insert the function body HERE
######################
   

#final_df is a 212 row x 64 col df of ints ranging from 0 to 6
#cell_df is an empty 212x64 df of empty strings to remove cell labelling behaviour
data = np.random.randint(0, 7, (212, 64))
final_df = pd.DataFrame(data, columns=[f"col{k}" for k in range(1, 65)])
bvals = [0,1,2,3,4,5,6,7]

colors_map = ['rgb(244,244,255)', #whiteish 
              'rgb(255, 128, 0)', #orange 
              'rgb(255,0,0)', #red 
              '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)
bvals = np.array(bvals)
tickvals = [np.mean(bvals[k:k+2]) for k in range(len(bvals)-1)]
ticktext  = ['param 1', 
             'param 2',
             'param 3',
             'param 4',
             'param 5',
             'param 6',
             'param 7']

fig_df = px.imshow(final_df.values, x= list(final_df.columns), 
                   y=list(final_df.index), color_continuous_scale=dcolorsc)
fig_df.update_traces(xgap=1, ygap=1)
fig_df.update_layout(height=900, xaxis={'title' : 'ID 1'},
    yaxis = {'title' : 'ID 2'},
    yaxis_nticks = len(final_df.index)//2,  #with all indices displayed as yaxis ticklabels you get a cluttered plot
    xaxis_nticks = len(final_df.columns),
    coloraxis_colorbar = dict(thickness=25, 
                              tickvals=tickvals, 
                              ticktext=ticktext));

import plotly.io as pio
pio.write_html(fig_df, "finalplot.html")

To inspect what attributes you can pass to heatmap via px.imshow(), j print help(px.imshow).
To decide the updates, display fig_df.data[0], respectively fig_df.layout just after the definition:

fig_df = px.imshow(final_df.values, x= list(final_df.columns), 
                   y=list(final_df.index), color_continuous_scale=dcolorsc)
1 Like

@empet Excellent, thank you very much! strangely this solution results in my color bar being twice as large (with respect to height) as my fig (irrespective of if i do y//2 or not). Is imshow autoscaling my fig size according to the fig x-axis, which is larger than the y-axis, do you know?

@TimothyK
You can add the colorbar_len=0.8 and/or any other len in [0,1] to update the colorbar height and colorbar_thickness=23 or 25 (pixels) for its thickness.

1 Like