Avoid expensive reload of the page

Dear all,

I’ve a page that is structured like this

There are more or less shown 600+ couples of rows that are similar. For a certain condition (depending on the values of the rows) I’m creating a radio button for some couples. I’m ending up with more or less 400 radio buttons. Apart of the fact that the loading of the page is very slow, but every time I click the radio button of a certain couple streamlit reloads the entire page (note that the radio button doesn’t influence the page, so it’s reloading exactly the same page) and it takes ~1 minutes for every radio button and this makes this page unuasble for a user…
Any suggest on how to avoid this? Code snippet here:

Is there a way maybe to cache only the st.write at line 212? This would avoid 80% of the useless reloading of the page. Thanks for helping :slight_smile:

Lorenzo

The following may or may not be applicable to your case:

  1. In most cases users do not need to have 600 tables and 400 radio buttons in the same page. Usually they are more confortable dealing with smaller chunks.
  2. ~1 min is way too much. The picture of your code suggests that you are repeating a lot of calculations and logic in each rerun. You can store the result and use it in succesive reruns instead.
  3. Widgets inside a form do not trigger a rerun when the user interacts with them.

Hi Goyo,

  1. Yes I know that from an UX point of view it’s quite a tragedy. In this page I’m showing, with some criteria before it, all the possible duplicates within a dataset. Usually I’d not expect this amount (400) but in this case they’re a lot.
    I know it’s something the user probably won’t do but I’d like to give him the possibility to choose to keep one row or the other.

  2. The answer is without any doubt yes, but the problem is that I can’t avoid this… imagine that I’m in the same state of the page, I’ve to do these computations somewhere and I know the parameters only when the page is shown the first time. How can I prevent a rerun to execute again all the computations if they are coded in the same page that is being rerun?

  3. This could be interesting, but can I store 400 variables in 400 different widgets in 400 different forms? And moreover, how could I encapsulate only the radio button in a form? I wouldn’t like to add also the submit button… is this doable?

Thanks for the feedback

I already told you. The first time you compute and store the results, next time you use the stored results, no need to compute the same thing again.

I am not aware of any limits in the number of widgets in a form or the number of forms. You can also put the 600 tables and 400 radio buttons inside just one form.

No. But then, what is an user supposed to do? Because, according to your description, just clicking the radio buttons isn’t going to take them anywhere. So they should do something else after that, shouldn’t they?

  1. I already told you. The first time you compute and store the results, next time you use the stored results, no need to compute the same thing again.

Yes, I know that I can do it somewhere else, but the problem is that the expensive computation is for the 99% due to the printing of the rows, that is something I can’t do in advance. So the point is yes, to compute the numbers outside the for and not every time the page reloads would acutally help, but will speed up the time only of 1%

  1. No. But then, what is an user supposed to do? Because, according to your description, just clicking the radio buttons isn’t going to take them anywhere. So they should do something else after that, shouldn’t they?

The idea is to store every radio result in a session_state array and when the page is refreshed I still have all the results and I can apply the transformation I want

I don’t see why you couldn’t also cache the numbers computed inside the for. That might require some refactoring. Unfortunately I can’t provide more specific advice without actual code that I can run.

But does the transformation depend on the radio button? Because if it does, that doesn’t match what you wrote before: “the radio button doesn’t influence the page, so it’s reloading exactly the same page”.

If the transformation depends on the radio button you only have to apply it when the radio button changes, not on every reload. Again, I can’t be more specific without code that I can run.

  1. I don’t see why you couldn’t also cache the numbers computed inside the for . That might require some refactoring. Unfortunately I can’t provide more specific advice without actual code that I can run.

Maybe the point I’ve missed to tell you is that when I say the “printing of the rows” , I’m not meaning the rows “Similarity of couple …” but the real records taken from the dataset.Is there a way to cache these writes? Here’s the code:

The writes are done at lines 203 and 215. Without these 2 lines I’d not have any problem in reloading the page but I mandatorily have to print these rows. item[1] and item[0] are row’s indexes.


  1. But does the transformation depend on the radio button? Because if it does, that doesn’t match what you wrote before: “the radio button doesn’t influence the page, so it’s reloading exactly the same page”.
    If the transformation depends on the radio button you only have to apply it when the radio button changes, not on every reload. Again, I can’t be more specific without code that I can run.

Imagine this use case: 600 couples of rows, 600 radio buttons and a session_state array of 600 elements.
Ideally what I want to do is: the user with the radio button choose to drop row1 or row2 or to do not drop anything. (Imagine the radio possibilities are Row1, Row2, None). The user performs this selection for every couple as every case of possible duplicate should be singularly handled.

Now the point is: I don’t want that the row is dropped as soon as the page is refreshed for the pressed radio button, I want all the rows to be dropped later. In order to this I store all the 600 results of the radio buttons in 600 elements of the session state array. So I’ll have this information always avaialable also in the future.

Imagine now the user has finished all the 600 choices and clicks save → it’s here that i can exploit the array and apply the transformation (dropping of the rows).

Is my use case a bit more clear now? Thank you!

Lorenzo

That’s pixels, not code. And even if I translated it to code it wouldn’t run.

I wrote code that fits your description the best I could. Using a form avoids the need for caching because clicking the radio buttons does not trigger a rerun.

There is still a noticeable lag when interacting with the radio buttons, which I think is unavoidable with so much stuff to render, but it is only a few seconds, not even close to one minute.

import uuid

def default_selection(couple):
    # do something smarter here
    return "None"

n_couples = 600

if st.button("Generate couples"):
    st.session_state.couples = [
        {"code": [uuid.uuid4(), uuid.uuid4()]}
        for _ in range(n_couples)
    ]

if "couples" not in st.session_state:
    st.stop()

with st.form(key="form"):
    for i, couple in enumerate(st.session_state.couples):
        st.dataframe(couple)
        key=f"selection_{i}"
        st.session_state[key] = default_selection(couple)
        st.radio(label="Select", options=["None", "First", "Second"], horizontal=True, key=key)
    save_button = st.form_submit_button(label="Save")

I still think this makes a terrible UX and that your efforts would be better spent in improving that.