Phantom line from previous plotly chart persisting on reload

Summary

My app has a form that allows users to select a variety of options and then click ‘submit’ to see two plotly charts based on their selections. I’m using plotly.express.line for these charts and for some reason when a user switches their selections on the form and reloads the page a pair of ‘phantom’ line artifacts from the previous set of charts persists on the new set.

Steps to reproduce

Code snippet:

import os
import pandas as pd
import streamlit as st
import plotly.express as px

dir_path = os.getcwd()
csv_path = os.path.join(dir_path, 'streamlit/signing_times/data/signing_data.csv')
source_data = pd.read_csv(csv_path)

test_strategies = source_data['Test Strategy'].dropna().unique().tolist()
devices = source_data['Model Name'].dropna().unique().tolist()

device_colors = {
    'Model One': '#00cc00',
    'Model T': '#006600',
    'Nano S': '#99ccff',
    'Nano X': '#0099ff',
    'Nano S+': '#0000ff',
    'Mk3': '#ff6666',
    'Mk4': '#cc0000'
    }

def makeFig(test_strategy, model_names, x_ax_metric, y_data, title):
    filtered_data = source_data.loc[
        source_data['Test Strategy'].isin([test_strategy])
        & source_data['Model Name'].isin(model_names)]

    ticks = filtered_data[x_ax_metric].dropna().unique().tolist()
    x_min = min(ticks)
    x_max = max(ticks)

    fig = px.line(
        filtered_data,
        x=x_ax_metric,
        y=y_data,
        markers=True,
        line_shape='spline',
        color='Model Name',
        color_discrete_map=device_colors,
        )
    fig.update_xaxes(
        range=[x_min * .5, x_max * 1.05],
        tickvals=ticks
        )
    fig.update_traces(connectgaps=False)
    fig.update_layout(
        autosize=True,
        title={
            'text' : title,
            'xanchor': 'center'
            }
        )
    return fig

with st.form(key='viz_settings'):
    test_strategy = st.selectbox("Test Strategy:", test_strategies, index=0)
    model_names = st.multiselect("Model Names:", devices, default=devices)
    x_ax_metric = st.selectbox("X-Axis Metric", ['UTXOs', 'Unsigned PSBT (kB)'], index=0)

    submit_button = st.form_submit_button()

if submit_button:

    if model_names:
        pre_fig = makeFig(
            test_strategy,
            model_names,
            x_ax_metric,
            y_data='Pre-Confirmation Duration (minutes)',
            title='Pre-Confirmation Duration'
            )

        st.plotly_chart(pre_fig, use_container_width=True, sharing='streamlit')

        post_fig = makeFig(
            test_strategy,
            model_names,
            x_ax_metric,
            y_data='Post-Confirmation Duration (minutes)',
            title='Post-Confirmation Duration'
            )

        st.plotly_chart(post_fig, use_container_width=True, sharing='streamlit')

    else: st.write("Error: Select at least one value for 'Model Names'")

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

  1. Go to my app using the link
  2. Click ‘Submit’ immediately with the default options
  3. Select ‘F2Pool…’ as ‘Test Strategy’
  4. Click ‘Submit’ again
  5. Note the phantom lines that show up from the previous chart

Debug info

  • Streamlit version: Streamlit, version 1.17.0
  • Python version: Python 3.9.16
  • Using: PipEnv
  • OS version: MacOS
  • Browser version: Brave

Requirements file

Pipfile contents:
[[source]]

url = “Simple index

verify_ssl = true

name = “pypi”

[packages]

streamlit = “*”

pandas = “*”

plotly = “*”

watchdog = “*”

[dev-packages]

[requires]

python_version = “3.9”

Links

Upon further debugging this artifact goes away when you click the full screen button on the plotly graph. Returning to the normal sized view also makes the artifact go away.

I’m not using st.cache but is the browser caching something to cause this?

Hi Samuel, I looked at this but I don’t quite see / understand the phantom lines. Could you send a screenshot of what you’re trying to say?

William

@willhuang


In the screenshot shown above you can see a lighter red line for Mk3 which shouldn’t exist on this version of the graph. It is the same line from the previous page load with different settings but on the version in the screenshot there is only one datapoint for Mk3 and thus there shouldn’t be a line at all.

Ahhh I see. There should be actually a release that resolves this that should be resolved today.
The streamlit version will be 1.18. Please upgrade to the latest streamlit when it’s released to fix this :slight_smile:

2 Likes

@Samuel_Bradbury , 1.18 should be released! Can you upgrade to the latest streamlit? :slight_smile:

Happy Streamlitting :balloon:

2 Likes

Awesome, that worked perfectly. Thanks @willhuang

i man!
I have the same phanton-line problem and updating to streamlit 1.18.1 did not fix it

image

Hi @taulant2 ,

Can u please give me some code to reproduce the issue?

Hey - sorry i’m a beginner in these forums.
Fixed it!

def getRebalLines(df):
    lines = []
    for idx in range(df.shape[0]):
        if df["rebalEvent"][idx]:
            lines.append(
                dict(
                    type="line",
                    x0=df["datetime"][idx],
                    x1=df["datetime"][idx],
                    y0=0,
                    y1=1,
                    yref="paper",
                    line=dict(color="rgba(169,169,169,0.7)"),
                )
            )
    return lines

With chatgpt i was able to resolve, sharing cuz maybe someone has similar struggles:

It looks like the issue with the function is that the lines parameter is being used as a mutable default argument. This can lead to unexpected behavior as the same list object is reused across multiple function calls.
In other words, if the lines argument is not passed explicitly to the function, the default value [] is used instead and any modifications made to lines inside the function will persist across subsequent function calls.

“why can’t i reset lines”

However, if you are still using the original versions of those functions (that used mutable default arguments), resetting lines=[] will not work as expected. This is because the default value of lines is created only once when the function is defined, and any subsequent modifications to it are persistent across function calls.
For example, if you have called the getRebalLines function multiple times without explicitly passing the lines argument, the same list object will be used and any lines added to it will persist across function calls. In this case, resetting lines=[] before plotting will not clear the list, since the original list object is still being used.

1 Like

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.