🏥 🏭 Working on the COVID-19 response in Canada? Plotly & the Canadian government can help you and your organization. Learn more and get in touch.

My hover_name and hover_data do not update during restyle

I am using plotly express in python. I simply want to use a dropdown to pick from different dataframes to display in a bar chart. I have simplified to just show two choices below.
I put the dataframes in a dictionary and I use the dropdown to pick which set of data to display.
Everything works EXCEPT the hover_name still shows the original value. So when I pick a different set of data, the bar chart updates, and the x and y values update, but the hover_name still shows what it was initialized as, which is very puzzling. Is there something fundamentally different about updating hover_name (or hover_data) from the restyle than updating x or y ?

The dataframes have 3 columns, code, desc, dcount

import plotly.express as px
import pandas as pd

dataframe_collection = {} 
d = {'code': ['D123', 'X4344'], 'dcount': [233, 411], 'desc' : ['Code for blisters' , 'Knee Pain']}
df = pd.DataFrame(data=d)
df
dataframe_collection['0-18'] = df 
d = {'code': ['J45', 'K44'], 'dcount': [512, 213], 'desc' : ['Shoulder Pain' , 'Meniscus Tear']}
df = pd.DataFrame(data=d)
dataframe_collection['19-25'] = df 

fig = px.bar(data_frame=dataframe_collection['0-18'], x='code', y='dcount',
             hover_name='desc',  
             labels={'desc':'Dx Description', 'dcount':'Dx Count', 'code':'Dx Code'}, height=400)
fig.update_layout(title = 'Dx Codes')


# Add dropdown
fig.update_layout(
    updatemenus=[
        dict(
            buttons=list([
                dict(
                    args=[{'y':[dataframe_collection['0-18']['dcount']],
                               'x':[dataframe_collection['0-18']['code']],
                                  'hover_name': [dataframe_collection['0-18']['desc']],
                               'type':'bar'}, [0]],
                   
                    label="Age 0-18",
                    method="restyle"
                ),
                dict(
                    args=[{'y':[dataframe_collection['19-25']['dcount']],
                               'x':[dataframe_collection['19-25']['code']],
                                      'hover_name': [dataframe_collection['19-25']['desc']],
                               'type':'bar'}, [0]],
                    label="Age 19-25",
                    method="restyle"
                )
            ]),
            direction="down",
            pad={"r": 10, "t": 10},
            showactive=True,
            x=0.1,
            xanchor="left",
            y=1.1,
            yanchor="top"
        ),
    ]
)


fig.show()

Hi @gregorius,

I cannot reproduce your example because you did not define ‘dataframe_collection’ in the pasted code :
NameError: name 'dataframe_collection' is not defined

Hence I suggest to display fig.data, before the line fig.update_layout. You’ll see how hovertemplate is defined for the initial data. Following the same rule, update the hovertemplate within args dicts, too.

Hi @gregorius welcome to the forum! Below is a working example. hover_name is a px-only argument, while the restyle method uses a lower-level API based on graph_objects. The solution I used was to use customdata to pass other data to the figure, and to build the hovertemplate using these customdata. Also, the way px builds the customdata imposes here to put every element in [].

As @empet says I did a lot of print(fig) to figure out a solution :-). Basically what you can use in restyle is what appears in fig.data when you print the figure.

import plotly.express as px
import pandas as pd

d = {'code': ['D123', 'X4344'], 'dcount': [233, 411], 'desc' : ['Code for blisters' , 'Knee Pain']}
df = pd.DataFrame(data=d)
dataframe_collection = {}
dataframe_collection['0-18'] = df 
d = {'code': ['J45', 'K44'], 'dcount': [512, 213], 'desc' : ['Shoulder Pain' , 'Meniscus Tear']}
df = pd.DataFrame(data=d)
dataframe_collection['19-25'] = df 

fig = px.bar(data_frame=dataframe_collection['0-18'], x='code', y='dcount',
             custom_data=['desc'],  
             labels={'desc':'Dx Description', 'dcount':'Dx Count', 'code':'Dx Code'}, height=400)
fig.update_layout(title = 'Dx Codes')
fig.update_traces(hovertemplate='<b>%{customdata[0]}</b><br>Dx Code=%{x}<br>Dx Count=%{y}')

# Add dropdown
fig.update_layout(
    updatemenus=[
        dict(
            buttons=list([
                dict(
                    args=[{'y':[dataframe_collection['0-18']['dcount']],
                               'x':[dataframe_collection['0-18']['code']],
                                  'customdata': [[['A'], ['B']]],
                               'type':'bar'}, [0]],
                   
                    label="Age 0-18",
                    method="restyle"
                ),
                dict(
                    args=[{'y':[dataframe_collection['19-25']['dcount']],
                            'x':[dataframe_collection['19-25']['code']],
                            'customdata': [[['C'], ['D']]],
                               'type':'bar'}, [0]],
                    label="Age 19-25",
                    method="restyle"
                )
            ]),
            direction="down",
            pad={"r": 10, "t": 10},
            showactive=True,
            x=0.1,
            xanchor="left",
            y=1.1,
            yanchor="top"
        ),
    ]
)


fig.show()

Made minor edit to add declaration of dataframe_collection, my apologies

Thank you @Emmanuelle ! I am now seeing my hover updated. I just have one additional question.

What is the proper way to pass the descriptions from my input data frame, rather than using the hard coded list in your example? So where you have: ‘customdata’: [[[‘A’], [‘B’]]]

How do I pass in a column from the dataframe, since I will have many values and many dataframes?

thanks!!

You can achieve this through list comprehension, like

x = pd.Series([1, 2, 3, 4])
[[el] for el in x]

Wonderful - thanks for all the help!