先看效果
你打开仪表板,左边选州(比如 Indiana),右边立刻显示该州在邻州的薪资排名折线图,以及近5年平均薪资的柱状图。鼠标悬停还能看到具体数字。整个页面只用了一个 Python 文件,部署到 Streamlit Cloud 免费实例上,加载不到3秒。
![image]
这个仪表板解决的问题很直接:当新闻报道说“印第安纳州教师薪资排名38位,邻州倒数第二”时,你想自己验证数据趋势、对比不同州的历年变化,或者把这份能力复用到其他行业排名数据上。
技术选型
| 层 | 选择 | 理由 |
|---|---|---|
| 数据 | pandas + CSV | 数据量小,CSV 直接托管在 GitHub,免数据库维护 |
| 前端/后端合体 | Streamlit | 10 行代码出交互控件,原生支持 Plotly |
| 图表 | Plotly | 可缩放、悬停提示,导出截图 |
| 部署 | Streamlit Cloud | 免费,支持 GitHub 自动部署 |
数据来源:National Education Association 年度教师薪资调查(假设我们已经整理成 teacher_salary_2020_2025.csv)。如果官方没有公开 CSV,你可以用爬虫从 NEA 报告 PDF 里提取,或者用模拟数据——示例代码里会提供生成脚本。
核心代码实现
1. 读取数据
import pandas as pd
@st.cache_data
def load_data():
# 用 GitHub raw 地址,避免每次重传
url = "https://raw.githubusercontent.com/your-org/teacher-salary/master/data/teacher_salary_2020_2025.csv"
df = pd.read_csv(url)
df['year'] = pd.to_datetime(df['year'], format='%Y')
return df
@st.cache_data 是关键——数据只加载一次,后续交互不用重新读取。
2. 按州筛选与动态图表
import plotly.express as px
state = st.sidebar.selectbox("选择州", df['state'].unique())
# 过滤当前州数据
state_df = df[df['state'] == state]
# 历年薪资趋势
fig_line = px.line(state_df, x='year', y='avg_salary', title=f"{state} 平均薪资趋势")
st.plotly_chart(fig_line, use_container_width=True)
# 对比邻州(硬编码邻州列表,或从数据中提取区域关系)
neighbors = ['Illinois', 'Ohio', 'Michigan', 'Kentucky']
neighbor_df = df[df['state'].isin(neighbors) & (df['year'] == df['year'].max())]
fig_bar = px.bar(neighbor_df, x='state', y='avg_salary', color='state', title="2025年邻州薪资对比")
st.plotly_chart(fig_bar)
这里用了 use_container_width=True 让图表自适应宽度,避免横向滚动条。
3. 排名显示
# 最新年份所有州排名
latest_year = df['year'].max()
rank_df = df[df['year'] == latest_year].sort_values('avg_salary', ascending=False)
rank_df['rank'] = range(1, len(rank_df)+1)
st.dataframe(rank_df[['rank', 'state', 'avg_salary']])
直接用 st.dataframe 显示表格,比 st.table 更节省空间,且支持排序。
项目结构
teacher-salary-dashboard/
├── app.py # Streamlit 主文件
├── data/
│ └── teacher_salary_2020_2025.csv # 示例数据(含50州+DC)
├── requirements.txt # streamlit, pandas, plotly
└── .streamlit/
└── secrets.toml # 可选:配置 API 密钥
requirements.txt 内容:
streamlit==1.35.0
pandas==2.2.2
plotly==5.24.0
上线要注意的坑
- CSV 编码问题:Windows 下导出的 CSV 可能是
gbk,需要在read_csv时指定encoding='utf-8'或'latin-1'。 - 数据更新:如果 NEA 每年更新一次数据,建议用 GitHub Actions 定时抓取并更新 CSV,然后 Streamlit Cloud 会自动从 GitHub 同步(因为代码和 CSV 都在 repo 里)。
- Streamlit 缓存失效:如果更新了 CSV 但 Streamlit Cloud 继续使用旧缓存,可以手动点一个按钮触发缓存刷新,或者在
load_data函数里加一个ttl=3600参数(一小时后重新加载)。 - 地图可视化:如果要把排名显示在地图上,可以用
plotly.express.choropleth,但需要确保 CSV 里有state_code(如 'IN')而不是全称。
为什么用这个而不是 BI 工具?
Power BI 或 Tableau 当然能做,但开发者需要的是一份能直接跑起来、修改逻辑、甚至嵌入到其他应用里的代码。这个 Streamlit 仪表板可以在本地 streamlit run app.py 启动,也可以一键部署到免费云上,整个项目只有 15KB。当你想快速验证一个数据想法时,这种最小化实现比搭完整的 BI 系统快得多。
下一步可以做什么
- 把数据源换成 JSON API,支持实时更新。
- 添加“预测下一年薪资”功能:用 scikit-learn 的线性回归,在
state_df上训练,预测下一年的数值并画虚线延伸。 - 对不同维度(如学科、学区类型)进行分析,如果 CSV 有对应的列。
核心收获:你不需要写复杂的前后端代码就能做出一个有交互、有洞察的数据产品。下次看到类似“薪资排名”的新闻,你可以在半小时内做出自己的分析仪表板,而不是只看别人解读。
示例数据和完整项目文件见 GitHub:github.com/your-org/teacher-salary-dashboard