Search code examples
pythonformattingplotlydata-visualizationplotly-python

Plotly: Display date in specified format in hoverlabel (customdata reading date as string)


import pandas as pd
from pandas import Timestamp
import plotly.express as px

df = pd.DataFrame({'continent': {127: 'South America',
  128: 'South America'},
 'date': {127: Timestamp('2021-03-01 00:00:00'),
  128: Timestamp('2021-03-26 00:00:00')},
 'total_cases': {127: 20465329.0,
  128: 23470911.0}})

fig = px.bar(df, x='continent', y='total_cases', animation_frame=df.date.astype(str), text='total_cases',
            custom_data=['date'])

fig.update_traces(hovertemplate='%{customdata}<br>%{y}')
for frame in fig.frames:
    frame.data[0].update(hovertemplate='%{customdata}<br>%{y}')
    
fig.show()

Running this code gives this hoverlabel output-

enter image description here

As you can see, the date is displayed as a long string. If I specify the d3 format and change hovertemplate to '%{customdata|%d %b %Y}<br>%{y}', the hoverlabel then looks like this-

enter image description here

I am not sure how to fix it. If instead of custom_data, I use the argument text in a similar fashion, it displays the date correctly. But I am already using text to display total_cases on the bar chart itself.


Solution

  • Turns out, custom_data of plotly.express and customdata of graph_objects are not exactly interchangeable. The solution is to remove custom_data from the px call and rather adding customdata to the trace calls. Here is the complete code-

    import pandas as pd
    from pandas import Timestamp
    import plotly.express as px
    
    df = pd.DataFrame({'continent': {127: 'South America',
      128: 'South America'},
     'date': {127: Timestamp('2021-03-01 00:00:00'),
      128: Timestamp('2021-03-26 00:00:00')},
     'total_cases': {127: 20465329.0,
      128: 23470911.0}})
    
    fig = px.bar(df, x='continent', y='total_cases', animation_frame=df.date.astype(str), text='total_cases')
    
    fig.update_traces(customdata=df['date'], hovertemplate='%{customdata|%d %b %Y}<br>%{y}')
    for frame in fig.frames:
        frame.data[0].update(hovertemplate='%{customdata|%d %b %Y}<br>%{y}')
        
    fig.show()
    

    enter image description here

    Edit:

    The solution will work for static dates but if the dates are to be updated in each frame, they have to be in a list and the list can be looped over by the existing loop (for frame in fig.frames:)