Issue with plotly tooltip when using x unified hovermode

Hi everyone,

I’m trying to build my first streamlit app, which will include a plotly graph objects line chart that displays both forecast and actual data.

When using the plotly x unified hovermode, I noticed a weird issue where the value for the first forecast year value is displayed in the tooltip for the last actual year (but no line is displayed, correctly). The opposite also occurs with the last actual year value displaying in the tooltip for the first forecast year.

E.g. in the below screenshot, 2022 should only have an ‘actual’ value, however the forecast value of 15 for 2023 is being displayed in the 2022 tooltip.

I played around with different year ranges and it seems to be related to the number of values to be plotted and the space in which to do so. E.g. the same range of values to be plotted may be ok with the legend hidden, but show the error when the legend is visible. Adding more values so that the x axis tick width is smaller can also result in the last 2 & first 2 hovers being duplicated, instead of just the last 1 & first 1.

python: 3.11.8
streamlit: 1.31.0
plotly: 5.18.0

I’m unable to reproduce the error when using the same code to display the chart in an ipynb without streamlit being involved.

Does anyone know why this may be occurring and how to fix it?

Code to reproduce is below. I’ve included a number of options for the forecast dataframe that show/don’t show the error.

import streamlit as st
import pandas as pd
import plotly.graph_objects as go

def create_trajectory_chart(actual_df, forecast_df):

    combined_df = pd.concat([actual_df, forecast_df], ignore_index=True)

    # Create an empty figure
    fig = go.Figure()
    # Add traces for each status
    for status in ['Actual', 'Forecast']:
        df_subset = combined_df[combined_df['status'] == status]
        if not df_subset.empty:
            fig.add_trace(go.Scatter(
                x=df_subset['year'],
                y=df_subset['percentage'],
                name=status
            ))

    # Update layout
    fig.update_layout(
        showlegend=False,
        hovermode="x unified"
    )

    return fig

# Actual data range
actual_data = {
    'year': [2018, 2019, 2020, 2021, 2022],
    'percentage': [10, 11, 12, 13, 14]
}

# This range does not produce error
forecast_data1 = {  
    'year': [2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042],
    'percentage': [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]
}

# This range does not produce error when legend is hidden, however error occurs when legend is shown
forecast_data2 = {  
    'year': [2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046],
    'percentage': [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38]
}

# This range produces an error even when the legend is hidden
forecast_data3 = {   
    'year': [2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047],
    'percentage': [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
}

# This range shows the error on the last two actual data points and first two forecast data points, even when the legend is hidden
forecast_data4 = {   
    'year': [2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077],
    'percentage': [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
}

# Create the dataframes
actual_df = pd.DataFrame(actual_data)
forecast_df = pd.DataFrame(forecast_data3)
actual_df['status'] = 'Actual'
forecast_df['status'] = 'Forecast'

# Create the chart
fig_trajectory_chart = create_trajectory_chart(actual_df, forecast_df)
st.plotly_chart(fig_trajectory_chart)
1 Like

Hi @bectech

It seems that when the number of elements in the forecast DataFrame increases, it is problematic and causes the error that you see. Particularly, the display of legends in the second DataFrame takes up horizontal space and thus leads to the error. Subsequent DataFrame also display error since the plot increases in size horizontally with more elements in the year column.

Would increasing the page width help to alleviate this? Can you try setting the page width to be in wide mode as follows:


st.set_page_config(layout="wide")

Hope this helps!

1 Like

Hi @dataprofessor,

Thanks for the response.

Unfortunately on my actual site I wish to have a forecast of around 100 years. This currently means that there’s three years each of actual and forecast values that have incorrect tooltips. So even if I could widen the page and then the chart enough that the issue is resolved, it would make the chart look ridiculous.

I could remove some datapoints e.g. only plot every 2 years, however I would prefer not to do this if at all possible.

1 Like

Does anyone else have any ideas before I submit this as a bug?

1 Like