How can I use extendData to update a heatmap?
I have a heatmap that I’d like to update efficiently, but I can’t figure out how to use extendData to make it happen.
Here’s my minimal example code, which starts by drawing the first 3 columns a diagonal heatmap. It should add more columns to the right as the button is pressed.
I’ve gotten this working using partial property updates and Patch(), so you can see my desired behavior. However, my actual use case has a very large heatmap, so redrawing the whole thing using Patch() is undesirable. I’d rather add columns as they are requested, with maxPoints removing the leftmost columns as needed to keep the figure a reasonable size.
I’m using dash 3.0.4.
"""Simple MRE for issues using extendData and heatmaps"""
import numpy as np
import plotly.graph_objects as go
from dash import Dash, Input, Output, Patch, State, callback, dcc, html
# Sample data for the heatmap
STARTING_COLS = 3
TOTAL_COLS = 10
DIAG_ARR = np.diag(np.ones(TOTAL_COLS)*100).astype(np.int8)
def main():
"""Initialize the Dash app and layout."""
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(figure=create_initial_heatmap(), id='heatmap'),
html.Button('Extend to the Right', id='update-button')
])
app.run(debug=True)
def create_initial_heatmap() -> go.Figure:
"""Create the initial heatmap figure with starting columns."""
return go.Figure(go.Heatmap(
z=DIAG_ARR[:, :STARTING_COLS],
x0=0,
dx=1,
y0=0,
dy=1,
zmin=0,
zmax=100
))
@callback(
Output('heatmap', 'figure'),
Input('update-button', 'n_clicks'),
State('heatmap', 'figure'),
prevent_initial_call=True
)
def update_heatmap_via_patch(n_clicks: int | None,
prev_fig: dict) -> Patch:
"""WORKING CALLBACK: update the heatmap data via partial property update"""
print(f'Number of clicks: {n_clicks}.')
print(f'Previous figure data: {prev_fig["data"]}')
heatmap_patch = Patch()
heatmap_patch.data[0].z = DIAG_ARR[:, :STARTING_COLS + n_clicks]
return heatmap_patch
# @callback(
# Output('heatmap', 'extendData'),
# Input('update-button', 'n_clicks'),
# State('heatmap', 'figure'),
# prevent_initial_call=True
# )
# def update_heatmap_extend_data(n_clicks: int | None,
# prev_fig: dict) -> dict:
# """NOT WORKING CALLBACK: update the heatmap data via extendData"""
# print(f'Number of clicks: {n_clicks}.')
# print(f'Previous figure data: {prev_fig["data"]}')
# if n_clicks is None or n_clicks > TOTAL_COLS - STARTING_COLS - 1:
# return {}
# col_to_add = min(STARTING_COLS + n_clicks, TOTAL_COLS - 1)
# data_to_add = DIAG_ARR[:, col_to_add].tolist()
# print(f'Adding column {col_to_add}: {data_to_add}')
# # Create new data to extend
# new_data = {
# 'updateData': {'z': data_to_add},
# 'traceIndices': [0],
# 'maxPoints': TOTAL_COLS}
# return new_data
if __name__ == '__main__':
main()