✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

Figure factory heatmap with buttons

I have two dataframes that I want to fit in an annotated heat map, and I want the user to be able to toggle between the two heat maps with buttons.

Problem is I can’t get update_layout to work as I expect it to. Here is my script:

a = np.random.rand(5, 5)
b = np.random.rand(5, 5)

fig = ff.create_annotated_heatmap()

a = [dict(a, 
        annotation_text=a,
           )]
b = [dict(b, 
        annotation_text=b,
           )]

fig.update_layout(
    updatemenus=[
        dict(
            type="buttons",
            buttons=list([
                dict(label="choose a",
                     method="update",
                     args=[{"visible": [True, False]},
                           {"title": "a is best",
                           }]),
                dict(label="choose b",
                     method="update",
                     args=[{"visible": [False, True]},
                           {"title": "b is better",
                           }]),
            ]),
        )
    ])

fig.show()

This returns:
TypeError: create_annotated_heatmap() missing 1 required positional argument: 'z'

What am I missing?

Hi @NRVA,

When you update annotations the method used in updatemenus is relayout, because only fig.layout is changed.
The update method is used when both data and layout are changed.
This an example on how to change the annotation text for an annotated heatmap:

import plotly.graph_objects as go
import numpy as np
import plotly.figure_factory as ff
import copy

z1_text =[['JWU', 'MFJ', 'DZM', 'CJN', 'CGA'],
 ['LMK', 'WAJ', 'HHV', 'CYE', 'XLC'],
 ['FHC', 'VCI', 'FGF', 'MVC', 'YWC'],
 ['HQN', 'BEQ', 'KZD', 'QXH', 'OZJ'],
 ['PCN', 'MEQ', 'PNX', 'NDA', 'YZG']]
z2_text = [['UUN', 'KKG', 'WPN', 'CBG', 'JOF'],
 ['NQG', 'PZQ', 'QLN', 'JCZ', 'GJA'],
 ['HHZ', 'POX', 'XFE', 'IVB', 'DJP'],
 ['KAY', 'FSZ', 'JBU', 'EYF', 'PSC'],
 ['FCH', 'LZN', 'DVB', 'JZF', 'VND']]

z2_t = [s for row in  z2_text for s in row]  #flatten z2_text

z = [[.1, .3, .5, .7, .9],
     [1, .8, .6, .4, .2],
     [.2, 0, .5, .7, .9],
     [.9, .8, .4, .2, 0],
     [.3, .4, .5, .7, 1]]

fig = ff.create_annotated_heatmap(z, annotation_text=z1_text, colorscale= 'Viridis')
fig.update_layout(width=600,height=600)

d = fig.to_dict()
old_annot = d['layout']['annotations']
new_annot = []

#update text in each annotation dict
for k, annot in enumerate(old_annot):
    c_annot = copy.deepcopy(annot) 
    c_annot['text'] = z2_t[k]
    new_annot.append(c_annot)    

fig.update_layout(updatemenus=[
                      dict(buttons=[dict(label="annot 1",
                                              method="relayout",
                                              args=[{"annotations": old_annot}]),
                                         dict(label="annot 2",
                                              method="relayout",
                                              args=[{"annotations": new_annot}])
                                   ])])

Thanks, but I also want to update/change the data (z).

@NRVA

In this case use the update method, and define an arg list of two dicts: the first one defines the changes in data, while the second one the changes in layout:

import plotly.graph_objects as go
import numpy as np
import plotly.figure_factory as ff
import copy
np.random.seed(123)

z1_text =[['JWU', 'MFJ', 'DZM', 'CJN', 'CGA'],
 ['LMK', 'WAJ', 'HHV', 'CYE', 'XLC'],
 ['FHC', 'VCI', 'FGF', 'MVC', 'YWC'],
 ['HQN', 'BEQ', 'KZD', 'QXH', 'OZJ'],
 ['PCN', 'MEQ', 'PNX', 'NDA', 'YZG']]
z2_text = [['UUN', 'KKG', 'WPN', 'CBG', 'JOF'],
 ['NQG', 'PZQ', 'QLN', 'JCZ', 'GJA'],
 ['HHZ', 'POX', 'XFE', 'IVB', 'DJP'],
 ['KAY', 'FSZ', 'JBU', 'EYF', 'PSC'],
 ['FCH', 'LZN', 'DVB', 'JZF', 'VND']]

z2_t = [s for row in  z2_text for s in row]  #flatten z2_text

z1 = [[.1, .3, .5, .7, .9],
     [1, .8, .6, .4, .2],
     [.2, 0, .5, .7, .9],
     [.9, .8, .4, .2, 0],
     [.3, .4, .5, .7, 1]]

z2 = list(np.random.rand(25).reshape((5, 5)))

fig = ff.create_annotated_heatmap(z1, annotation_text=z1_text, colorscale= 'Viridis')
fig.update_layout(width=600,height=600)

d = fig.to_dict()
old_annot = d['layout']['annotations']
new_annot = []

#update text in each annotation dict
for k, annot in enumerate(old_annot):
    c_annot = copy.deepcopy(annot) 
    c_annot['text'] = z2_t[k]
    new_annot.append(c_annot)    

fig.update_layout(updatemenus=[
                      dict(buttons=[dict(label="annot 1",
                                              method="update",
                                              args=[{"z":[z1]},
                                                    {"annotations": old_annot}]),
                                         dict(label="annot 2",
                                              method="update",
                                              args=[{"z": [z2]},
                                                   {"annotations": new_annot}])
                                   ])])

For more details on relayout, restyle, and update see this notebook and those at the links inserted in the introduction: https://chart-studio.plotly.com/~empet/15608