Trigger a full-script rerun from inside a fragment
Streamlit 允许你将函数转换为片段,这些片段可以独立于完整脚本重新运行。当用户与片段内的小部件交互时,只有该片段会重新运行。有时,你可能希望从片段内部触发完整脚本的重新运行。为此,可以在片段内调用st.rerun。
Applied concepts
- 使用片段根据用户输入重新运行部分或全部应用程序。
Prerequisites
-
您的Python环境中必须安装以下内容:
streamlit>=1.37.0 -
你应该有一个干净的工作目录,名为
your-repository。 -
你应该对片段和
st.rerun有一个基本的理解。
Summary
在这个例子中,你将构建一个应用程序来显示销售数据。该应用程序有两组依赖于日期选择的元素。一组元素显示所选日期的信息。另一组元素显示相关月份的信息。如果用户在一个月内更改日期,Streamlit 只需要更新第一组元素。如果用户选择不同月份的日期,Streamlit 需要更新所有元素。
您将把特定日期的元素收集到一个片段中,以避免当用户在同一月份内更改日期时重新运行整个应用程序。如果您想直接跳到片段函数的定义,请参阅构建一个函数以显示每日销售数据。

以下是您将要构建的内容:
import streamlit as st
import pandas as pd
import numpy as np
from datetime import date, timedelta
import string
import time
@st.cache_data
def get_data():
"""Generate random sales data for Widget A through Widget Z"""
product_names = ["Widget " + letter for letter in string.ascii_uppercase]
average_daily_sales = np.random.normal(1_000, 300, len(product_names))
products = dict(zip(product_names, average_daily_sales))
data = pd.DataFrame({})
sales_dates = np.arange(date(2023, 1, 1), date(2024, 1, 1), timedelta(days=1))
for product, sales in products.items():
data[product] = np.random.normal(sales, 300, len(sales_dates)).round(2)
data.index = sales_dates
data.index = data.index.date
return data
@st.fragment
def show_daily_sales(data):
time.sleep(1)
with st.container(height=100):
selected_date = st.date_input(
"Pick a day ",
value=date(2023, 1, 1),
min_value=date(2023, 1, 1),
max_value=date(2023, 12, 31),
key="selected_date",
)
if "previous_date" not in st.session_state:
st.session_state.previous_date = selected_date
previous_date = st.session_state.previous_date
st.session_state.previous_date = selected_date
is_new_month = selected_date.replace(day=1) != previous_date.replace(day=1)
if is_new_month:
st.rerun()
with st.container(height=510):
st.header(f"Best sellers, {selected_date:%m/%d/%y}")
top_ten = data.loc[selected_date].sort_values(ascending=False)[0:10]
cols = st.columns([1, 4])
cols[0].dataframe(top_ten)
cols[1].bar_chart(top_ten)
with st.container(height=510):
st.header(f"Worst sellers, {selected_date:%m/%d/%y}")
bottom_ten = data.loc[selected_date].sort_values()[0:10]
cols = st.columns([1, 4])
cols[0].dataframe(bottom_ten)
cols[1].bar_chart(bottom_ten)
def show_monthly_sales(data):
time.sleep(1)
selected_date = st.session_state.selected_date
this_month = selected_date.replace(day=1)
next_month = (selected_date.replace(day=28) + timedelta(days=4)).replace(day=1)
st.container(height=100, border=False)
with st.container(height=510):
st.header(f"Daily sales for all products, {this_month:%B %Y}")
monthly_sales = data[(data.index < next_month) & (data.index >= this_month)]
st.write(monthly_sales)
with st.container(height=510):
st.header(f"Total sales for all products, {this_month:%B %Y}")
st.bar_chart(monthly_sales.sum())
st.set_page_config(layout="wide")
st.title("Daily vs monthly sales, by product")
st.markdown("This app shows the 2023 daily sales for Widget A through Widget Z.")
data = get_data()
daily, monthly = st.columns(2)
with daily:
show_daily_sales(data)
with monthly:
show_monthly_sales(data)

Build the example
Initialize your app
-
在
your_repository中,创建一个名为app.py的文件。 -
在终端中,将目录更改为
your_repository并启动您的应用程序。streamlit run app.py由于您仍然需要添加代码,您的应用程序将是空白的。
-
在
app.py中,编写以下内容:import streamlit as st import pandas as pd import numpy as np from datetime import date, timedelta import string import time你将按以下方式使用这些库:
- 你将在
pandas.DataFrame中处理销售数据。 - 你将使用
numpy生成随机销售数字。 - 数据将具有
datetime.date索引值。 - 销售的产品将从 "Widget A" 到 "Widget Z",因此你将使用
string以便轻松访问字母字符串。 - 可选:为了在最后增加强调效果,你将使用
time.sleep()来减慢速度并查看片段的工作情况。
- 你将在
-
保存您的
app.py文件并查看正在运行的应用程序。 -
点击“始终重新运行”或在运行的应用中按下“A”键。
当您保存对
app.py的更改时,您的运行预览将自动更新。您的预览仍然会是空白的。返回您的代码。
Build a function to create random sales data
首先,你将定义一个函数来随机生成一些销售数据。如果你只想复制函数,可以跳过这一部分。
@st.cache_data
def get_data():
"""Generate random sales data for Widget A through Widget Z"""
product_names = ["Widget " + letter for letter in string.ascii_uppercase]
average_daily_sales = np.random.normal(1_000, 300, len(product_names))
products = dict(zip(product_names, average_daily_sales))
data = pd.DataFrame({})
sales_dates = np.arange(date(2023, 1, 1), date(2024, 1, 1), timedelta(days=1))
for product, sales in products.items():
data[product] = np.random.normal(sales, 300, len(sales_dates)).round(2)
data.index = sales_dates
data.index = data.index.date
return data
-
使用
@st.cache_data装饰器并开始你的函数定义。@st.cache_data def get_data(): """为Widget A到Widget Z生成随机销售数据"""你不需要不断地重新随机化数据,因此缓存装饰器将随机生成数据一次并将其保存在Streamlit的缓存中。当你的应用程序重新运行时,它将使用缓存的值而不是重新计算新数据。
-
定义产品名称列表并为每个产品分配一个平均日销售值。
product_names = ["Widget " + letter for letter in string.ascii_uppercase] average_daily_sales = np.random.normal(1_000, 300, len(product_names)) products = dict(zip(product_names, average_daily_sales)) -
对于每个产品,使用其平均每日销售额随机生成一整年的每日销售额值。
data = pd.DataFrame({}) sales_dates = np.arange(date(2023, 1, 1), date(2024, 1, 1), timedelta(days=1)) for product, sales in products.items(): data[product] = np.random.normal(sales, 300, len(sales_dates)).round(2) data.index = sales_dates data.index = data.index.date在最后一行,
data.index.date去除了时间戳,因此索引将显示干净的日期。 -
返回随机销售数据。
return data -
可选:通过调用并显示数据来测试您的函数。
data = get_data() data保存您的
app.py文件以查看预览。删除这两行或将它们保留在应用程序的末尾,以便在继续时更新。
Build a function to show daily sales data
由于每日销售数据会随着每次新日期的选择而更新,您将把这个函数转换为一个片段。作为一个片段,它可以独立于应用程序的其他部分重新运行。您将在这个片段中包含一个st.date_input小部件,并监视更改月份的日期选择。当片段检测到所选月份的变化时,它将触发整个应用程序的重新运行,以便所有内容都可以更新。
@st.fragment
def show_daily_sales(data):
time.sleep(1)
selected_date = st.date_input(
"Pick a day ",
value=date(2023, 1, 1),
min_value=date(2023, 1, 1),
max_value=date(2023, 12, 31),
key="selected_date",
)
if "previous_date" not in st.session_state:
st.session_state.previous_date = selected_date
previous_date = st.session_state.previous_date
st.session_state.previous_date = selected_date
is_new_month = selected_date.replace(day=1) != previous_date.replace(day=1)
if is_new_month:
st.rerun()
st.header(f"Best sellers, {selected_date:%m/%d/%y}")
top_ten = data.loc[selected_date].sort_values(ascending=False)[0:10]
cols = st.columns([1, 4])
cols[0].dataframe(top_ten)
cols[1].bar_chart(top_ten)
st.header(f"Worst sellers, {selected_date:%m/%d/%y}")
bottom_ten = data.loc[selected_date].sort_values()[0:10]
cols = st.columns([1, 4])
cols[0].dataframe(bottom_ten)
cols[1].bar_chart(bottom_ten)
-
使用一个
@st.fragment装饰器并开始你的函数定义。@st.fragment def show_daily_sales(data):由于你的数据在片段重新运行时不会改变,你可以将数据作为参数传递给片段。
-
可选:添加
time.sleep(1)以减慢函数速度并展示片段的工作原理。time.sleep(1) -
添加一个
st.date_input小部件。selected_date = st.date_input( "选择一个日期", value=date(2023, 1, 1), min_value=date(2023, 1, 1), max_value=date(2023, 12, 31), key="selected_date", )您的随机数据是针对2023年的,因此设置最小和最大日期以匹配。为小部件使用一个键,因为片段外部的元素将需要此日期值。在处理片段时,最好使用会话状态来传递信息进出片段。
-
在会话状态中初始化
"previous_date"以比较每个日期选择。if "previous_date" not in st.session_state: st.session_state.previous_date = selected_date -
将之前的日期选择保存到一个新变量中,并更新Session State中的
"previous_date"。previous_date = st.session_state.previous_date st.session_state.previous_date = selected_date -
如果月份发生变化,调用
st.rerun()。is_new_month = selected_date.replace(day=1) != previous_date.replace(day=1) if is_new_month: st.rerun() -
显示选定日期的最佳销售者。
st.header(f"Best sellers, {selected_date:%m/%d/%y}") top_ten = data.loc[selected_date].sort_values(ascending=False)[0:10] cols = st.columns([1, 4]) cols[0].dataframe(top_ten) cols[1].bar_chart(top_ten) -
显示从选定日期起的最差销售者。
st.header(f"Worst sellers, {selected_date:%m/%d/%y}") bottom_ten = data.loc[selected_date].sort_values()[0:10] cols = st.columns([1, 4]) cols[0].dataframe(bottom_ten) cols[1].bar_chart(bottom_ten) -
可选:通过调用函数并显示数据来测试你的函数。
data = get_data() show_daily_sales(data)保存你的
app.py文件以查看预览。删除这两行或将它们保留在应用程序的末尾,以便在继续时更新。
Build a function to show monthly sales data
最后,让我们构建一个函数来显示月度销售数据。它将类似于你的show_daily_sales函数,但不需要是片段。你只需要在整个应用程序重新运行时重新运行此函数。
def show_monthly_sales(data):
time.sleep(1)
selected_date = st.session_state.selected_date
this_month = selected_date.replace(day=1)
next_month = (selected_date.replace(day=28) + timedelta(days=4)).replace(day=1)
st.header(f"Daily sales for all products, {this_month:%B %Y}")
monthly_sales = data[(data.index < next_month) & (data.index >= this_month)]
st.write(monthly_sales)
st.header(f"Total sales for all products, {this_month:%B %Y}")
st.bar_chart(monthly_sales.sum())
-
开始你的函数定义。
def show_monthly_sales(data): -
可选:添加
time.sleep(1)以减慢函数速度并展示片段的工作原理。time.sleep(1) -
从会话状态中获取选定的日期,并计算本月和下个月的第一天。
selected_date = st.session_state.selected_date this_month = selected_date.replace(day=1) next_month = (selected_date.replace(day=28) + timedelta(days=4)).replace(day=1) -
显示所选月份内所有产品的每日销售值。
st.header(f"Daily sales for all products, {this_month:%B %Y}") monthly_sales = data[(data.index < next_month) & (data.index >= this_month)] st.write(monthly_sales) -
显示选定月份内每种产品的总销售额。
st.header(f"Total sales for all products, {this_month:%B %Y}") st.bar_chart(monthly_sales.sum()) -
可选:通过调用函数并显示数据来测试你的函数。
data = get_data() show_daily_sales(data) show_monthly_sales(data)保存你的
app.py文件以查看预览。完成后删除这三行。
Put the functions together together to create an app
让我们并排显示这些元素。您将在左侧显示每日数据,在右侧显示每月数据。
-
如果您在代码末尾添加了可选行来测试您的函数,请现在清除它们。
-
给你的应用一个宽布局。
st.set_page_config(layout="wide") -
获取你的数据。
data = get_data() -
为您的应用程序添加标题和描述。
st.title("Daily vs monthly sales, by product") st.markdown("此应用程序显示2023年Widget A到Widget Z的每日销售额。") -
创建列并调用函数以显示数据。
daily, monthly = st.columns(2) with daily: show_daily_sales(data) with monthly: show_monthly_sales(data)
Make it pretty
现在,您已经拥有一个使用片段来防止不必要地重绘月度数据的应用程序。然而,页面上的内容没有对齐,因此您可以插入一些容器使其更美观。在每个显示函数中添加三个容器。
-
在
show_daily_sales函数中添加三个容器以固定元素的高度。@st.fragment def show_daily_sales(data): time.sleep(1) with st.container(height=100): ### 添加容器 ### selected_date = st.date_input( "选择日期", value=date(2023, 1, 1), min_value=date(2023, 1, 1), max_value=date(2023, 12, 31), key="selected_date", ) if "previous_date" not in st.session_state: st.session_state.previous_date = selected_date previous_date = st.session_state.previous_date previous_date = st.session_state.previous_date st.session_state.previous_date = selected_date is_new_month = selected_date.replace(day=1) != previous_date.replace(day=1) if is_new_month: st.rerun() with st.container(height=510): ### 添加容器 ### st.header(f"最佳销售, {selected_date:%m/%d/%y}") top_ten = data.loc[selected_date].sort_values(ascending=False)[0:10] cols = st.columns([1, 4]) cols[0].dataframe(top_ten) cols[1].bar_chart(top_ten) with st.container(height=510): ### 添加容器 ### st.header(f"最差销售, {selected_date:%m/%d/%y}") bottom_ten = data.loc[selected_date].sort_values()[0:10] cols = st.columns([1, 4]) cols[0].dataframe(bottom_ten) cols[1].bar_chart(bottom_ten) -
在
show_monthly_sales函数中添加三个容器以固定元素的高度。def show_monthly_sales(data): time.sleep(1) selected_date = st.session_state.selected_date this_month = selected_date.replace(day=1) next_month = (selected_date.replace(day=28) + timedelta(days=4)).replace(day=1) st.container(height=100, border=False) ### 添加容器 ### with st.container(height=510): ### 添加容器 ### st.header(f"所有产品的每日销售额, {this_month:%B %Y}") monthly_sales = data[(data.index < next_month) & (data.index >= this_month)] st.write(monthly_sales) with st.container(height=510): ### 添加容器 ### st.header(f"所有产品的总销售额, {this_month:%B %Y}") st.bar_chart(monthly_sales.sum())第一个容器创建了空间,以便与
show_daily_sales函数中的输入小部件协调。
Next steps
继续美化示例。尝试使用st.plotly_chart或st.altair_chart为您的图表添加标签并调整其高度。
还有问题吗?
我们的 论坛 充满了有用的信息和Streamlit专家。