Have user specify weights for features they've chosen in a multiselect

Summary

In the app I want the user first to select the features they want to emphasise in a recommender systen(there are 30 features in total).
They should then choose the weights for each feature using sliders,

Steps to reproduce

Code snippet:

def specify_weight(feature):
    btn = st.button('Ok', key = feature)
    sel_col, disp_col = st.columns(2)
    btn_pressed = False
    while not btn_pressed:
        weight = sel_col.slider(f"How important is this to you?", min_value=1, max_value=10, value=5, step=1, format= '%d', key = feature)  
        if btn:
            btn_pressed = True
    return weight
def main():
    features = ['potato', 'spinach', 'chicken']
    for feature in features:
        weight = specify_weight(feature)
        print(f"weight for feature{feature} is: {weight}")

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

Expected behavior:

I want the user to select a weight for feature 1, press ‘Done’ and then repeat the process for the rest of the features. This should work with an arbitrary number of features, i.e. the number of features should not be bruteforced.

Actual behavior:

I have tried different methods to do this. I first tried taking the list of features as an argument, and use a for loop to select the weights, but kept getting the error message:
" ‘list’ object has no attributes slider’.
I guess that the app thought that I was trying to add the slider to the list, but I was trying to add the return value.

I then chose to work more modularly and changed the function to take in one feature at a time. I tried using the same slider, and added the feature name as key. However, it now keeps giving me the error message: "streamlit.errors.DuplicateWidgetID: There are multiple widgets with the same `key=‘potato’ ".

I thought the app would only create one slider for feature one, since the while loop is running. I am a beginner at Streamlit, so there is most likely a lot that I have yet to learn.

I would be really grateful for help and guiding answers.
Hope you all are having a great day!

Debug info

  • Streamlit version: version 1.17.0
  • Python version: 3.9.1
  • Conda Env
  • OS version: macOS 12.3.1
  • Browser version: Version 109.0.5414.119 (Official Build) (x86_64)

Requirements file

Using Conda? PipEnv? PyEnv? Pex? Share the contents of your requirements file here.
Not sure what a requirements file is? Check out this doc and add a requirements file to your app.

Links

  • Link to your GitHub repo:
  • Link to your deployed app:

Additional information

If needed, add any other context about the problem here.

You’ll need to take the widgets outside of the while loop. It’s not going to automatically multi-thread or wait for user input quite like that. As written, this will fail because of assigning the same key to multiple widgets. If the unique key requirement were ignored, Streamlit would just keep creating the first slider over and over, making dozens or hundreds of the same input before a user even has the chance to react.

I would more commonly see a workflow like this:

import streamlit as st

def main():
    features = ['potato', 'spinach', 'chicken']
    for feature in features:
        st.slider(f"How important is this to you?", min_value=1, max_value=10, value=5, step=1, format= '%d', key = feature)
    
    st.header('Lorem ipsum foo')
    st.header('')

    for feature in features:
        st.write(f"weight for feature {feature} is: {st.session_state[feature]}")

main()

If you need wait for user confirmation to proceed, I think it would be simplest to put a single confirmation button at the end of the grouping, but let me know if you have some other requirements.

Thank you very much! :slight_smile: How could I do to save the weights? The goal is to save them in a dictionary (this is then used in the model). I tried to save the values from the slider to a list in a for loop and then use zip, but got the error that " ‘list’ object has no attribute slider" so I assume that streamlit was trying to add the slider itself to the list. Also tried casting the slider to an int value using int() but that did not work either.

You can access the values from session state as demonstrated in the last line for the write statement:

st.session_state[feature]

The value of the widget is stored in session state under the key of the widget (no other attribute). Note that if you move to another page or stop rendering the widget for some reason, the value in session state would go away with the widget, in which case you should create some other key in session state into which you can copy all the values.

thank you very much!! it works now.
Hope you’re having a really nice week-end :slight_smile:

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