Streamlit 定制化的 Web 应用
目录
1. Streamlit 简介
1.1 什么是 Streamlit?
Streamlit 是一个开源的 Python 库,它使得为机器学习和数据科学项目创建和共享美观、定制化的 Web 应用变得极其简单快捷。你无需任何前端知识(HTML, CSS, JavaScript),只需使用 Python 即可构建交互式的数据应用。
1.2 为什么选择 Streamlit?
- 快速开发: 将数据脚本转化为可共享的 Web 应用通常只需要几行代码。
- 纯 Python: 无需学习前端框架,专注于 Python 和数据。
- 交互性: 内置丰富的交互式控件(滑块、按钮、下拉菜单等)。
- 易于共享: 可以轻松部署到 Streamlit Community Cloud 或其他平台。
- 代码即应用: 应用的 UI 和逻辑直接由 Python 脚本定义,所见即所得。
- 活跃社区: 拥有庞大且活跃的社区支持。
1.3 核心特性
- 即时反馈: 修改代码并保存后,浏览器中的应用会自动刷新。
- 数据缓存: 智能缓存机制避免重复计算和加载数据,提升性能。
- 内置控件: 提供大量开箱即用的 UI 控件。
- 布局灵活: 支持侧边栏、列、选项卡等布局方式。
- 主题定制: 支持浅色、深色主题及自定义主题。
1.4 目标用户
- 数据科学家: 快速将模型、分析结果可视化并分享给他人。
- 机器学习工程师: 构建模型演示、数据标注工具等。
- 数据分析师: 创建交互式报告和仪表板。
- Python 开发者: 需要快速构建简单 Web 应用的场景。
2. 核心概念
2.1 数据流与脚本执行模型
Streamlit 应用的核心在于其独特的执行模型:每当用户与应用中的控件交互(如点击按钮、拖动滑块)或者源代码被修改时,整个 Python 脚本会从头到尾重新执行一次。
这听起来可能效率低下,但 Streamlit 通过智能的缓存机制(见 2.5)来优化性能。这种模型的优点是极大地简化了应用开发,开发者只需编写线性的 Python 脚本,Streamlit 负责处理 Web 应用的复杂性。
2.2 “魔法”命令 (Magic Commands)
Streamlit 支持“魔法”命令。如果你在脚本中单独写一个变量或一个字面量值(如 DataFrame、字符串、数字),Streamlit 会自动调用 st.write() 将其显示在应用中。
import streamlit as st
import pandas as pd
df = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
"这是一个 Markdown 文本" # Magic command, 等同于 st.write("这是一个 Markdown 文本")
df # Magic command, 等同于 st.write(df) 或 st.dataframe(df)
2.3 控件 (Widgets) 与交互
控件是用户与 Streamlit 应用交互的主要方式。每个控件本质上是一个 Python 函数调用,其返回值通常是用户输入的值。
name = st.text_input("请输入你的名字:")
if name:
st.write(f"你好, {name}!")
age = st.slider("选择你的年龄:", 0, 100, 25) # 最小值, 最大值, 默认值
st.write(f"你的年龄是: {age}")
clicked = st.button("点我")
if clicked:
st.write("按钮被点击了!")
当用户与控件交互(例如输入文本、移动滑块、点击按钮),Streamlit 会:
- 记录控件的新值。
- 重新运行整个脚本。
- 在重新运行时,控件函数会返回用户设置的新值。
2.4 状态管理 (Session State)
由于每次交互都会重新运行脚本,局部变量无法在多次运行之间保持其值。为了解决这个问题,Streamlit 提供了 st.session_state。这是一个类似字典的对象,用于存储需要在脚本重新运行时保留的信息(会话状态)。
import streamlit as st
# 初始化 session state 中的计数器(如果它不存在)
if 'count' not in st.session_state:
st.session_state.count = 0
increment = st.button('增加计数')
if increment:
st.session_state.count += 1
st.write('计数器: ', st.session_state.count)
2.5 缓存 (Caching)
为了避免在每次脚本重新运行时都执行耗时的操作(如加载大型数据集、训练模型、访问 API),Streamlit 提供了缓存装饰器:
@st.cache_data: 用于缓存可序列化的数据(如 DataFrame, list, dict)。当函数的输入参数或函数代码改变时,缓存才会失效。@st.cache_resource: 用于缓存不方便或不应该被序列化的全局资源(如数据库连接、机器学习模型)。当函数代码改变时,缓存才会失效(对输入参数不敏感)。
import streamlit as st
import pandas as pd
import time
@st.cache_data # 缓存数据加载函数
def load_data(url):
df = pd.read_csv(url)
time.sleep(3) # 模拟耗时操作
return df
data_url = "https://raw.githubusercontent.com/streamlit/demo-data/master/uber-raw-data-sep14.csv.gz"
df = load_data(data_url)
st.dataframe(df.head())
st.button("再次运行脚本") # 点击按钮会重新运行,但 load_data 不会再次执行耗时操作
3. 安装教程
3.1 环境要求
- Python 3.8 - 3.11
- PIP 包管理器
建议在虚拟环境中安装 Streamlit,以避免与其他项目的依赖冲突。
创建和激活虚拟环境 (可选但推荐):
# Linux/macOS
python3 -m venv .venv
source .venv/bin/activate
# Windows (cmd)
python -m venv .venv
.venv\Scripts\activate.bat
# Windows (PowerShell)
python -m venv .venv
.venv\Scripts\Activate.ps1
3.2 使用 pip 安装
在你的终端或命令行中运行:
pip install streamlit
3.3 验证安装
安装完成后,运行以下命令:
streamlit hello
这会启动一个演示应用,并在你的默认浏览器中打开一个新的标签页。如果能看到 Streamlit 的欢迎页面和演示,说明安装成功。
3.4 运行第一个应用
-
创建一个 Python 文件,例如
my_first_app.py。 -
在文件中写入以下代码:
import streamlit as st import numpy as np import pandas as pd st.title('我的第一个 Streamlit 应用') st.write("这是一个简单的 Streamlit 应用示例。") # 使用魔法命令显示 DataFrame df = pd.DataFrame({ '第一列': [1, 2, 3, 4], '第二列': [10, 20, 30, 40] }) df # 添加一个滑块控件 x = st.slider('选择一个值', 0, 10) st.write('你选择的值是:', x) # 绘制一个简单的图表 st.line_chart(np.random.randn(20, 2)) -
在终端中,切换到包含
my_first_app.py的目录,然后运行:streamlit run my_first_app.py -
Streamlit 会启动一个本地 Web 服务器,并在浏览器中打开你的应用。
4. 主要组件与常用 API
Streamlit 提供了丰富的 API 来构建应用的 UI。
4.1 文本显示
st.title(): 应用标题st.header(): 一级标题st.subheader(): 二级标题st.write(): 通用显示函数,可以显示文本、Markdown、数字、DataFrame、图表等。st.markdown(): 渲染 Markdown 格式的文本。st.code(): 显示代码块。st.latex(): 显示 LaTeX 公式。
4.2 数据展示
st.dataframe(): 显示可交互的 DataFrame (支持排序)。st.table(): 显示静态表格。st.json(): 显示 JSON 数据。st.metric(): 显示指标(KPI),可带变化量。
4.3 图表绘制
Streamlit 支持多种图表库:
- 内置图表:
st.line_chart(),st.area_chart(),st.bar_chart(),st.scatter_chart()(基于 Vega-Lite)。 - 第三方库集成:
st.pyplot(): 显示 Matplotlib 图表。st.plotly_chart(): 显示 Plotly 图表。st.altair_chart(): 显示 Altair 图表。st.vega_lite_chart(): 显示 Vega-Lite 图表。- (以及 Bokeh, PyDeck 等)
4.4 输入控件
这些控件用于收集用户输入,它们的返回值是用户提供的值。
st.button(): 按钮。st.checkbox(): 复选框。st.radio(): 单选按钮。st.selectbox(): 下拉选择框。st.multiselect(): 多选下拉框。st.slider(): 滑块。st.select_slider(): 在一组离散值中选择的滑块。st.text_input(): 单行文本输入框。st.text_area(): 多行文本输入框。st.number_input(): 数字输入框。st.date_input(): 日期选择器。st.time_input(): 时间选择器。st.file_uploader(): 文件上传控件。st.color_picker(): 颜色选择器。
4.5 布局与容器
用于组织页面元素:
st.sidebar: 在页面左侧创建一个侧边栏,控件可以放在这里。add_selectbox = st.sidebar.selectbox( "你想联系谁?", ("邮件", "电话", "短信") )st.columns(): 创建并排的列。col1, col2, col3 = st.columns(3) with col1: st.header("第一列") st.write("内容...") with col2: st.header("第二列") # ...st.tabs(): 创建选项卡布局。tab1, tab2 = st.tabs(["图表", "数据"]) with tab1: st.line_chart(...) with tab2: st.dataframe(...)st.expander(): 创建可折叠的区域。st.container(): 创建一个多元素容器,可以用于控制元素插入顺序。st.empty(): 创建一个占位符,后续可以用内容替换或清空。
4.6 媒体文件
st.image(): 显示图片。st.audio(): 播放音频文件。st.video(): 播放视频文件。
4.7 状态与控制流
st.form(): 创建一个表单,可以批量提交一组输入控件的值。在表单内的控件交互不会触发脚本重新运行,只有点击表单的提交按钮 (st.form_submit_button) 时才会。st.spinner(): 显示一个加载指示器,用于耗时操作。st.progress(): 显示一个进度条。st.error(),st.warning(),st.info(),st.success(): 显示不同类型的消息框。st.exception(): 显示异常信息。st.stop(): 停止脚本执行。st.rerun(): 手动触发脚本重新运行。
5. 进阶功能与策略
5.1 深入 Session State
st.session_state 是构建复杂交互应用的关键。
- 初始化: 务必在使用前检查 key 是否存在并进行初始化,通常在脚本开头完成。
- 回调函数: 许多控件(如
st.button,st.slider)支持on_change参数,可以绑定一个回调函数。当控件值改变时,回调函数会在脚本重新运行 之前 执行,这对于更新st.session_state非常有用。
import streamlit as st
def increment_counter():
st.session_state.count += 1
if 'count' not in st.session_state:
st.session_state.count = 0
st.button('增加', on_click=increment_counter) # 使用回调
st.write('计数器: ', st.session_state.count)
5.2 高效使用缓存 (@st.cache_data vs @st.cache_resource)
- 选择合适的装饰器:
- 加载 DataFrame、处理 JSON/API 结果等返回普通数据的函数,使用
@st.cache_data。 - 初始化数据库连接、加载大型机器学习模型等返回复杂对象或需要全局共享资源的函数,使用
@st.cache_resource。
- 加载 DataFrame、处理 JSON/API 结果等返回普通数据的函数,使用
- 缓存粒度: 尽量将耗时操作封装在独立的函数中,并对该函数应用缓存。
- 注意参数:
@st.cache_data对输入参数敏感。如果参数是可变对象(如列表、字典)且在函数内部被修改,可能会导致意外的缓存行为。考虑传递不可变对象或对象的副本。 - 清除缓存: 有时需要手动清除缓存,可以在 Streamlit 应用的右上角菜单中找到 “Clear cache” 选项。
5.3 优化应用布局
- 使用
st.sidebar: 将全局设置、导航或不常变化的控件放在侧边栏,保持主区域清爽。 - 使用
st.columns或st.tabs: 合理组织内容,避免页面过长。 - 使用
st.expander: 将详细信息或次要内容隐藏在可展开区域内。 - 使用
st.container: 控制元素的添加顺序,或将一组相关的元素打包。
5.4 连接数据源 (数据库, API)
Streamlit 本身不直接提供数据库或 API 连接器,但可以无缝集成标准的 Python 库:
- 数据库: 使用
SQLAlchemy,psycopg2(PostgreSQL),mysql-connector-python(MySQL),sqlite3等库连接数据库。将数据库连接和查询操作封装在函数中,并考虑使用@st.cache_resource缓存连接,使用@st.cache_data缓存查询结果。 - API: 使用
requests或httpx等库访问外部 API。同样,将 API 调用封装并使用@st.cache_data缓存结果,避免频繁请求。
import streamlit as st
import requests
import pandas as pd
@st.cache_data(ttl=600) # 缓存 10 分钟
def get_api_data(api_url):
response = requests.get(api_url)
response.raise_for_status() # 检查请求是否成功
return response.json()
# 示例:获取 GitHub 星标信息
try:
data = get_api_data("https://api.github.com/repos/streamlit/streamlit")
st.write("Streamlit GitHub Repo Stars:", data.get('stargazers_count'))
except requests.exceptions.RequestException as e:
st.error(f"API 请求失败: {e}")
5.5 应用部署 (Streamlit Community Cloud)
Streamlit Community Cloud 是官方提供的免费部署平台,非常适合共享公共应用。
- 准备代码: 将你的 Streamlit 应用代码 (
.py文件) 和依赖文件 (requirements.txt) 托管在 GitHub 公开仓库中。 - 注册/登录: 访问 share.streamlit.io 并使用 GitHub 账号登录。
- 部署应用: 点击 “New app”,选择你的 GitHub 仓库、分支和主应用文件。
- 配置 (可选): 可以设置 Python 版本和高级设置。
- 点击 “Deploy!” Streamlit 会自动拉取代码、安装依赖并运行你的应用。
其他部署选项包括:Heroku, AWS, Google Cloud, Azure, Docker 容器等,这些通常需要更多的配置。
5.6 自定义组件 (Brief Mention)
对于更高级的需求,如果内置控件不满足,可以创建自己的 Streamlit 组件。这需要一些前端知识(React),允许你将任何 Web 技术集成到 Streamlit 应用中。这是一个更深入的主题,可以查阅官方文档了解更多。
6. 策略示例 (代码驱动)
6.1 示例一:交互式数据探索器
这个例子展示了如何上传 CSV 文件,并使用控件进行筛选和可视化。
import streamlit as st
import pandas as pd
import numpy as np
st.title("交互式数据探索器")
uploaded_file = st.file_uploader("上传你的 CSV 文件", type=["csv"])
if uploaded_file is not None:
# 使用缓存加载数据
@st.cache_data
def load_csv(file):
return pd.read_csv(file)
df = load_csv(uploaded_file)
st.header("原始数据")
st.dataframe(df)
st.header("数据筛选与可视化")
# 使用列进行布局
col1, col2 = st.columns(2)
with col1:
# 选择列进行可视化
all_columns = df.columns.tolist()
selected_columns = st.multiselect("选择要显示的列:", all_columns, default=all_columns[:min(5, len(all_columns))])
if selected_columns:
st.dataframe(df[selected_columns])
else:
st.warning("请至少选择一列")
with col2:
# 基于数值列进行过滤
numeric_columns = df.select_dtypes(include=np.number).columns.tolist()
if numeric_columns:
filter_column = st.selectbox("选择用于过滤的数值列:", numeric_columns)
min_val = float(df[filter_column].min())
max_val = float(df[filter_column].max())
value_range = st.slider(
f"选择 {filter_column} 的范围:",
min_value=min_val,
max_value=max_val,
value=(min_val, max_val) # 默认选择整个范围
)
# 应用过滤
filtered_df = df[(df[filter_column] >= value_range[0]) & (df[filter_column] <= value_range[1])]
st.write(f"根据 {filter_column} 过滤后的数据 ({value_range[0]} - {value_range[1]}):")
st.dataframe(filtered_df)
else:
st.write("数据中没有数值列可供过滤。")
st.header("简单图表")
if numeric_columns and len(numeric_columns) >= 2:
plot_col1 = st.selectbox("选择 X 轴 (数值):", numeric_columns, index=0)
plot_col2 = st.selectbox("选择 Y 轴 (数值):", numeric_columns, index=min(1, len(numeric_columns)-1))
chart_type = st.radio("选择图表类型:", ('折线图', '散点图'))
# 根据选择绘制图表 (使用过滤后的数据或原始数据)
data_to_plot = filtered_df if numeric_columns and filter_column else df
data_to_plot = data_to_plot[[plot_col1, plot_col2]].dropna() # 确保没有 NaN 值
if not data_to_plot.empty:
if chart_type == '折线图':
st.line_chart(data_to_plot.rename(columns={plot_col1:'index'}).set_index('index'))
elif chart_type == '散点图':
st.scatter_chart(data_to_plot, x=plot_col1, y=plot_col2)
else:
st.warning("选择的数据无法绘制图表(可能为空或包含NaN)。")
elif numeric_columns:
st.bar_chart(df[numeric_columns[0]].value_counts()) # 对第一个数值列绘制条形图
else:
st.write("没有足够的数值列来绘制图表。")
else:
st.info("请先上传一个 CSV 文件。")
6.2 示例二:参数可调的可视化
这个例子使用滑块控制生成的数据,并用 Plotly 进行可视化。
import streamlit as st
import numpy as np
import plotly.graph_objects as go
st.title("参数可调的可视化")
st.sidebar.header("参数设置")
# 使用 Session State 存储参数默认值,以便在重新运行时保持
if 'num_points' not in st.session_state:
st.session_state.num_points = 50
if 'frequency' not in st.session_state:
st.session_state.frequency = 1.0
if 'amplitude' not in st.session_state:
st.session_state.amplitude = 1.0
if 'noise_level' not in st.session_state:
st.session_state.noise_level = 0.1
# 创建控件,并将它们的当前值绑定回 Session State
num_points = st.sidebar.slider(
"数据点数量:", 10, 500, key='num_points'
)
frequency = st.sidebar.slider(
"频率 (Hz):", 0.1, 5.0, step=0.1, key='frequency'
)
amplitude = st.sidebar.slider(
"振幅:", 0.1, 5.0, step=0.1, key='amplitude'
)
noise_level = st.sidebar.slider(
"噪声水平:", 0.0, 1.0, step=0.05, key='noise_level'
)
# 根据当前参数生成数据
@st.cache_data # 缓存数据生成过程
def generate_data(n, freq, amp, noise):
x = np.linspace(0, 10, n)
y = amp * np.sin(2 * np.pi * freq * x) + np.random.randn(n) * noise
return x, y
x_data, y_data = generate_data(num_points, frequency, amplitude, noise_level)
# 使用 Plotly 绘制图表
fig = go.Figure()
fig.add_trace(go.Scatter(x=x_data, y=y_data, mode='lines+markers', name='数据'))
fig.update_layout(
title='生成的数据波形',
xaxis_title='X 值',
yaxis_title='Y 值',
legend_title='图例'
)
st.plotly_chart(fig, use_container_width=True)
st.write("当前参数:")
st.write(f"- 数据点数量: {num_points}")
st.write(f"- 频率: {frequency:.1f} Hz")
st.write(f"- 振幅: {amplitude:.1f}")
st.write(f"- 噪声水平: {noise_level:.2f}")
6.3 示例三:简单的机器学习模型演示
这个例子加载一个预训练的简单模型(例如 Scikit-learn 的 Iris 分类器),并提供输入控件让用户输入特征值,然后显示预测结果。
import streamlit as st
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
import joblib # 或者使用 pickle
# --- 模型训练与保存 (通常在单独的脚本中完成) ---
# def train_and_save_model():
# iris = load_iris()
# X, y = iris.data, iris.target
# model = RandomForestClassifier(random_state=42)
# model.fit(X, y)
# joblib.dump(model, 'iris_model.joblib')
# print("Model trained and saved as iris_model.joblib")
# train_and_save_model() # 运行一次来生成模型文件
# -------------------------------------------------
st.title("鸢尾花分类器演示")
# 加载预训练模型 (使用缓存)
@st.cache_resource # 使用 cache_resource 因为模型通常是复杂对象
def load_model(model_path='iris_model.joblib'):
try:
model = joblib.load(model_path)
return model
except FileNotFoundError:
st.error(f"模型文件 '{model_path}' 未找到。请先运行训练脚本生成模型。")
return None
except Exception as e:
st.error(f"加载模型时出错: {e}")
return None
model = load_model()
if model:
iris = load_iris() # 加载数据集信息以获取特征名称和类别名称
feature_names = iris.feature_names
target_names = iris.target_names
st.sidebar.header("输入鸢尾花特征:")
input_features = {}
for i, feature in enumerate(feature_names):
# 使用特征名称作为 key,避免 session state 冲突
feature_key = f"feature_{i}"
# 合理设置默认值和范围 (可以基于 iris 数据集的统计信息)
min_val = float(iris.data[:, i].min())
max_val = float(iris.data[:, i].max())
mean_val = float(iris.data[:, i].mean())
input_features[feature] = st.sidebar.slider(
feature,
min_value=min_val,
max_value=max_val,
value=mean_val, # 默认使用平均值
key=feature_key
)
# 将用户输入转换为模型需要的格式 (numpy array)
input_data = pd.DataFrame([input_features])
input_array = input_data.values
st.subheader("用户输入的特征:")
st.write(input_data)
# 进行预测
if st.button("预测类别"):
try:
prediction_proba = model.predict_proba(input_array)
prediction = model.predict(input_array)[0] # 获取预测的类别索引
st.subheader("预测结果:")
st.write(f"预测的鸢尾花类别是: **{target_names[prediction]}**")
st.subheader("预测概率:")
proba_df = pd.DataFrame(prediction_proba, columns=target_names)
st.dataframe(proba_df)
# 可视化概率
st.bar_chart(proba_df.T)
except Exception as e:
st.error(f"预测时发生错误: {e}")
else:
st.warning("模型未能加载,无法进行预测。")
注意: 运行此示例前,需要先确保已安装 scikit-learn 和 joblib (pip install scikit-learn joblib),并已生成 iris_model.joblib 文件(可以通过取消注释并运行 train_and_save_model() 函数一次来创建)。
7. 最佳实践与技巧
- 代码组织: 使用函数来组织代码块,提高可读性和可维护性。对于大型应用,考虑将不同页面或功能模块拆分到不同的 Python 文件中(Streamlit 支持多页应用)。
- 善用缓存: 对所有耗时的操作(数据加载、复杂计算、模型加载/预测、API 调用)使用
@st.cache_data或@st.cache_resource。 - 理解 Session State: 合理使用
st.session_state来保存跨脚本运行的状态,避免在每次交互时丢失用户进度或设置。优先使用回调函数来更新状态。 - 优化性能:
- 只加载必要的数据。
- 对大数据集进行采样或聚合后再显示。
- 避免在主脚本中进行过于密集的计算,将其放入缓存函数中。
- 用户体验:
- 使用布局元素(sidebar, columns, tabs, expander)组织内容。
- 提供清晰的指示和反馈(使用
st.info,st.warning,st.spinner等)。 - 使用
st.form来组合多个输入,避免每次小改动都触发重新运行。
- 依赖管理: 始终使用
requirements.txt文件来管理项目依赖,特别是需要部署时。 - 错误处理: 使用
try...except块来捕获潜在的错误(如文件未找到、API 请求失败、数据格式错误),并向用户显示友好的错误消息 (st.error)。
8. 总结
Streamlit 是一个强大而简洁的工具,它极大地降低了构建交互式数据应用的门槛。通过理解其核心概念(脚本重运行、控件、状态管理、缓存)并掌握其主要 API,你可以快速地将数据分析、机器学习模型转化为易于分享和使用的 Web 应用。无论是进行快速原型设计、内部工具开发,还是构建面向公众的演示,Streamlit 都是一个值得考虑的优秀选择。
9. 学习资源
- Streamlit 官方文档: https://docs.streamlit.io/ - 最权威、最全面的资源。
- Streamlit Cheat Sheet: https://docs.streamlit.io/library/cheatsheet - 快速查找常用命令。
- Streamlit Gallery: https://streamlit.io/gallery - 查看社区创建的精彩应用,获取灵感。
- Streamlit Community Forum: https://discuss.streamlit.io/ - 提问、交流、寻求帮助的地方。
希望这份深度解析能帮助你更好地理解和使用 Streamlit!
2万+

被折叠的 条评论
为什么被折叠?



