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)
CodePudding user response:
I suspect that the annotations are very expensive for plotly to render. It may be that even if you are passing a 212x64 array of empty strings to the annotation_text
argument, plotly still has to go through them all to determine that there are no annotations to add.
I created a 212x64 array with random integers from 0-6 and it was also very slow to render in my browser and I got the same "page not responding" warnings as you.
When I used go.heatmap
, I was able to obtain what appears to be the same plot as ff.create_annotated_heatmap
, and this improved the execution time from 5-6 seconds down to 0.66 seconds, and it also responds much faster in the browser.
This seems more straightforward than creating an annotated heatmap and not using the annotations (is there a particular reason you need ff.create_annotated_heatmap instead of go.heatmap?)
import numpy as np
import pandas as pd
import plotly.figure_factory as ff
import plotly.graph_objects as go
import time
start_time = time.time()
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
## recreate your dfs
np.random.seed(42)
final_df = pd.DataFrame(np.random.randint(0,6,size=(212, 64)), columns=list(range(64)))
# cell_df = final_df.applymap(lambda x: annot_map.get(x, x))
cell_df = pd.DataFrame(np.array(['']*212*64).reshape(212,64), columns=list(range(64)))
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 = go.Figure([go.Heatmap(
z=final_df,
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.show()
print(f"Program executed in {time.time() - start_time} seconds")
## original code with figure_factory annotated heatmap: Program executed in 5.351915121078491 seconds
## modified code with graph_objects heatmap: Program executed in 0.6627509593963623 seconds
# fig_df.write_html(results_file_df)