I have a dataframe in data_editor with a checkbox column. And I want to display the average age above the data_editor table in 2 metrics (checked rows and average age).
If I show the metrics below the data_editor, then the correct value is shown. The metrics above the data_editor are not refreshed (they are one click behind).
How can I refresh the metrics above the data_editor showing the correct metrics (the same values as the metrics below the data_editor)?
I ran into this issue with my app as well. The two solutions I went with were 1) Put the display after the check boxes, 2) rerun the page with a submit or update button. I know a lot of the community is requesting the ability to update certain portions of the page without reruning the entire script I would look into st.fragments and modal dialogs.
There may be other options but you can add an on_change argument to your data_editor. See the section Use Callbacks to update Session State. It would be something like
My go-to method is usually a callback as @S11 recommended. That will update the value before the script runs so it will be correct from the start of the page load.
Alternatively, a standard technique in Streamlit is to use containers to change the order of elements on the screen.
Create a container before your dataframe
After your dataframe, calculate your values.
Then, call st.metric and render it into the container above your data editor.
I don’t know your whole use-case, but just for reference, if you’re using the data editor just for row selections, version 1.35.0 of Streamlit does have this capability for st.dataframe and that would open up a third option: just get the selections directly from Session State from the dataframe’s key. This is also possible with st.data_editor, but is considerably more messy.
thanks for your reply, but
this is not working, still the same behavior when I add the callback function. Maybe this is not working because of the data editor.
code with the callback:
import streamlit as st
from streamlit import session_state as ss
import pandas as pd
def on_change_function():
ss.checked_rows = len(checked_rows)
if 'checked_rows' not in ss:
ss.checked_rows = 0
if 'average_age' not in ss:
ss.average_age = 0
#metrics above data_editor
st.metric(value=ss.checked_rows, label = "checked rows")
st.metric(value=ss.average_age, label = "average age checked rows")
# data sample
data = {
'Name': ['John', 'Anna', 'Peter', 'Linda'],
'Age': [28, 24, 35, 32],
'Year': [2034, 2023, 2023, 2014]
}
df = pd.DataFrame(data)
#insert checkbox column
df.insert(0, "check", False)
de = st.data_editor(df, key = 'data', on_change = on_change_function)
checked_rows= de[de['check']]
#checked rows
ss.checked_rows = len(checked_rows)
#average year
if len(checked_rows) != 0:
avg_age = checked_rows.agg(Age=('Age', 'average')).reset_index()
ss.average_age = avg_age['Age'][0]
#metrics below data_editor
st.metric(value=ss.checked_rows, label = "checked rows")
st.metric(value=ss.average_age, label = "average age checked rows")```
In the use case I have an update button, but it’s less user friendly that the user has to click a button (and it is hard to tell if the metrics are in sync with the detail data). So therefor I want to update the metric automatically. But if it is not working, I will go with your solution!
Since you are adding checked_rows to the SessionState before your metric (in the if statement), the initial value will be set to 0 (in the if statement). You can add an initial value = <> argument in st.metric if you want, but Streamlit will give you a warning that you are setting the value twice. After that, the st.session_state.checked_rows will continue to be updated as the value since that’s the key of the metric. Sorry I didn’t check your key names earlier, have now fixed my comment earlier.
MetricMixin.metric() got an unexpected keyword argument ‘key’
or
StreamlitAPIException: Values for st.button, st.download_button, st.file_uploader, st.data_editor, st.chat_input, and st.form cannot be set using st.session_state.
my code:
import streamlit as st
from streamlit import session_state as ss
import pandas as pd
def on_change_function()-> None:
ss.checked_rows = len(checked_rows)
if 'checked_rows' not in ss:
ss.checked_rows = 0
if 'average_age' not in ss:
ss.average_age = 0
#metrics above data_editor
# st.metric(value=ss.checked_rows, label = "checked rows")
# st.metric(key = ss.checked_rows, label = 'checked rows')
# st.metric(value=ss.average_age, label = "average age checked rows")
# data sample
data = {
'Name': ['John', 'Anna', 'Peter', 'Linda'],
'Age': [28, 24, 35, 32],
'Year': [2034, 2023, 2023, 2014]
}
df = pd.DataFrame(data)
#insert checkbox column
df.insert(0, "check", False)
# de = st.data_editor(df, key = 'data', on_change = on_change_function)
de = st.data_editor(df, key = 'checked_rows', on_change = on_change_function)
checked_rows= de[de['check']]
#checked rows
ss.checked_rows = len(checked_rows)
#average year
if len(checked_rows) != 0:
avg_age = checked_rows.agg(Age=('Age', 'average')).reset_index()
ss.average_age = avg_age['Age'][0]
#metrics below data_editor
# st.metric(value=ss.checked_rows, label = "checked rows")
# st.metric(value=ss.average_age, label = "average age checked rows")``````