Avoiding chart redraws on irrelevant changes

Summary

My app has many plotly charts. The problem is, whenever I change an input that controls one chart, all the other charts are also redrawn. This can be quite slow, even if I cache the functions that produce the plotly figures. I guess “st.plotly_chart” still has to translate the plotly figure to web elements, and that takes time. Is there anyway around this? Thanks!

Steps to reproduce

Code snippet:

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


@st.experimental_memo
def load_data():
    # US births 2014, from an R data set repo
    return pd.read_csv('https://vincentarelbundock.github.io/Rdatasets/csv/openintro/births14.csv')


@st.experimental_memo
def figure(df_in, facet_in='marital'):
    return px.scatter(df_in, x='gained', y='weight', color='habit', facet_col=facet_in)


df = load_data()
st.header('Test chart redraw')

st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))
st.plotly_chart(figure(df))

facet = st.radio('facet', ['marital', 'sex', 'whitemom'])
st.plotly_chart(figure(df, facet))

  1. Scroll to the bottom of the page
  2. Change the radio button.
  3. Note how all the charts are redrawn, even though the radio button should only affect the last chart.

Expected behavior:

Only the last chart is redrawn and updated.

Actual behavior:

The affected chart is slow to update, because streamlit redraws all the charts above it first, even though those figures haven’t changed.

Note: if this doesn’t happen on your computer, then just add more charts until you see the delay. (And be thankful that you have a fast computer :wink:)

Debug info

  • Streamlit version: 1.14.0
  • Python version: 3.7.9
  • OS version: Win 10
  • Browser version: Chrome 107.0.5304.88

Requirements file

plotly
pandas
streamlit==1.14.0

1 Like

Hi @HHest, would you be willing to add a line to your streamlit app

st.write(st.__version__) just to triple-check that you’re actually running the app with 1.14.0?

There was a bug that was recently fixed which should keep plotly charts for re-rendering unnecessarily.

I tried your code, but with 100 plotly charts, on streamlit 1.14.0, and didn’t see any visible delay or redrawing of other charts when manipulating one the charts. This made me wonder if you might actually be running the app with a different version of streamlit.

1 Like

Thanks for replying, @blackary. I got “1.14.0” when I did that, and the lag behavior is still there. I got the same behavior on the Edge browser, too.

(I took a screencast, but this website doesn’t support WEBM, which is a bit unfortunate since streamlit itself uses WEBM.)
… Have used an online converter to get a WEBP file. Caveat emptor…
… Well, that didn’t work, but we have the screenshot anyway…
… Got it working with an animated gif. It’s not as clear, but still you can see the “Running” images appear and the graphs fading in and out. (This is with 11 graphs above the final graph.)

streamlit-check_streamlit_graph

Here’s my pip freeze output, in case you want to see it.

altair==4.2.0
attrs==22.1.0
backports.zoneinfo==0.2.1
blinker==1.5
cachetools==5.2.0
certifi==2022.9.24
charset-normalizer==2.1.1
click==8.1.3
colorama==0.4.6
commonmark==0.9.1
decorator==5.1.1
entrypoints==0.4
gitdb==4.0.9
GitPython==3.1.29
idna==3.4
importlib-metadata==5.0.0
importlib-resources==5.10.0
Jinja2==3.1.2
jsonschema==4.17.0
MarkupSafe==2.1.1
numpy==1.21.6
packaging==21.3
pandas==1.3.5
Pillow==9.3.0
pkgutil_resolve_name==1.3.10
plotly==5.11.0
protobuf==3.20.3
pyarrow==10.0.0
pydeck==0.8.0
Pygments==2.13.0
Pympler==1.0.1
pyparsing==3.0.9
pyrsistent==0.19.2
python-dateutil==2.8.2
pytz==2022.6
pytz-deprecation-shim==0.1.0.post0
requests==2.28.1
rich==12.6.0
semver==2.13.0
six==1.16.0
smmap==5.0.0
streamlit==1.14.0
tenacity==8.1.0
toml==0.10.2
toolz==0.12.0
tornado==6.2
typing_extensions==4.4.0
tzdata==2022.6
tzlocal==4.2
urllib3==1.26.12
validators==0.20.0
watchdog==2.1.9
zipp==3.10.0

:man_facepalming: I apologize, @HHest – I did not read your instructions very carefully about when you were seeing the bug. I am now seeing exactly the behavior you described.

The bad news is that this is simply the default behavior of streamlit – when one input is changed, it reruns the whole app. Normally you solve issues like this by using st.experimental_memo to cache slow parts of your app’s code (e.g. fetching data). And, st.experimental_memo has recently been updated so that you actually can cache charts like this, but in my experimentation it didn’t seem to add much performance benefit (presumably actually rendering all those charts on the screen, even if they are pre-cached, is still slow).

So, I’m stumped to find a better way. The good news is, that bug fix I mentioned probably means it’s a lot faster than it used to be, but I’m not sure how you can avoid a visible re-render with a large number of charts in your app.

I see. Thanks for confirming this issue, @blackary.

My actual situation is worse than the example because of the complexity of my own graphs. :frowning:

One thing I’ve tried is to group graphs under different tabs, but unfortunately Streamlit would draw the contents of the tabs even if they are not active. It would have helped if Streamlit would check and run only the code inside the active tab. (Codes to render tabs are clearly isolated in their “with” contexts, so I am hoping this is possible?)

Eventually though, one might still run into a situation where the complexity of the graphs on the same page would cause this lag issue to appear.

Honestly, I love Streamlit for its simplicity, but this limitation is giving me pause. It seems costly to redraw everything every time any input is changed, and at some point that cost will catch up to you.

You can use radio buttons or a selectbox instead of tabs, so that only the code related to the selected option will run. If you want radio buttons that look like tabs, or tabs that behave like radio buttons, you are not the first one. There are some components that might help with that (I didn’t use any of them so I don’t really know).

3 Likes

Thanks for the radio button suggestion, @Goyo. This will help performance, and with luck, it would be enough.

Wanted to upvote this. Having the same issue (just with two graphs, so nothing so tragic luckily). It seems like this is a somewhat blocking issue for some use cases. I imagine there isn’t a super easy dev fix, but it seems you could make headway if tabs rendered separately from each other.

1 Like