数据分析毕业设计选题实战:从真实数据集到可部署分析系统的完整路径
本科毕设最怕“玩具项目”:数据静态、结果一次性、展示靠截图。下面用一次完整的电商用户行为分析实战,带你把“跑个图”升级成“可访问、可交互、可复现”的在线系统,让答辩老师一眼看到工程化能力。
1. 背景痛点:为什么你的数据分析毕设总被嫌“浅”
- 无实时性:Excel 或 Jupyter 截图只能证明“跑通过”,换批数据就翻车。
- 无接口:没有 REST 端点,前端同学无法嵌入,后续扩展为零。
- 不可复现:依赖藏在本地 site-packages,换电脑就报错;路径写死,一键迁移失败。
- 无业务闭环:停留在描述性统计,缺“行动建议”或“预测下一跳”,价值感稀薄。
一句话:缺“工程化”思维,导致技术深度和展示效果双双失分。
2. 技术选型对比:Notebook vs. Streamlit vs. Flask
| 维度 | Jupyter Notebook | Streamlit | Flask |
|---|---|---|---|
| 交互成本 | 最低,cell 即运行 | 中等,写脚本即可 | 高,需前后端分离 |
| 部署难度 | 几乎无法直接上线 | 一键streamlit run+ 云托管 | 需 Gunicorn + Nginx |
| 状态管理 | 无,变量全在内存 | Session 自动封装 | 完全手动 |
| 样式定制 | Markdown 有限 | 组件式,主题可 CSS 注入 | Jinja2 自由,但工作量大 |
| 学习曲线 | 0 | 1 天 | 3-5 天 |
| 适合场景 | 探索、报告 | 快速 MVP、答辩 Demo | 生产级、高并发 |
结论:毕设周期 8-10 周,选 Streamlit 性价比最高;若团队有 Web 基础,可直接 Flask,后期更容易加登录、权限等模块。
3. 核心实现:30 分钟搭出可访问的用户行为看板
下面以阿里天池“UserBehavior”CSV(约 100 万条日志,字段:user_id,item_id,category,behavior_type,timestamp)为例,演示从数据到指标再到交互的闭环。
3.1 环境初始化
python -m venv venv source venv/bin/activate pip install -r requirements.txtrequirements.txt(锁定版本,可复现):
streamlit==1.28.2 pandas==2.0.3 numpy==1.24.3 matplotlib==3.7.1 seaborn==0.12.23.2 数据加载与清洗(clean_data.py)
import pandas as pd from pathlib import Path RAW = Path("data/raw/UserBehavior.csv") CLEAN = Path("data/clean/behavior.parquet") def load_raw() -> pd.DataFrame: """读原始 CSV,统一列名并转时间戳""" df = pd.read_csv( RAW, header=None, names=["user_id", "item_id", "category", "behavior", "ts"], dtype={"behavior": "category"}, parse_dates=["ts"], ) return df def clean(df: pd.DataFrame) -> pd.DataFrame: """异常值处理:去重、剔除未来时间、补齐缺失值""" df = df.drop_duplicates() max_ts = pd.Timestamp("2018-12-31") # 数据集约定范围 df = df[df["ts"] <= max_ts] df["behavior"] = df["behavior"].cat.remove_unused_categories() return df if __name__ == "__main__": df = clean(load_raw()) CLEAN.parent.mkdir(parents=True, exist_ok=True) df.to_parquet(CLEAN) # 列存加速后续读取运行一次即可生成中间文件,后续分析直接读 Parquet,秒级加载。
3.3 关键指标计算(metrics.py)
import pandas as pd from pathlib import Path DATA = Path("data/clean/behavior.parquet") def daily_retention(day0: str, span: int = 7) -> pd.Series: """ 计算 span 日内留存率(以 day0 注册为基准) 返回 Series(index=日期, value=留存率) """ df = pd.read_parquet(DATA) base = df[df["ts"].dt.date == pd.to_datetime(day0).date()]["user_id"].unique() result = {} for i in range(1, span + 1): d = pd.to_datetime(day0) + pd.Timedelta(days=i) active = df[df["ts"].dt.date == d.date()]["user_id"].unique() result[d.strftime("%Y-%m-%d")] = len(set(active) & set(base)) / len(base) return pd.Series(result) def conversion_funnel(start, end) -> pd.DataFrame: """ 计算 pv->cart->fav->buy 四步漏斗 返回 DataFrame 含每一步人数与转化率 """ df = pd.read_parquet(DATA) df = df[(df["ts"] >= start) & (df["ts"] <= end)] pv = df["user_id"].nunique() cart = df[df["behavior"] == "cart"]["user_id"].nunique() fav = df[df["behavior"] == "fav"]["user_id"].nunique() buy = df[df["behavior"] == "buy"]["user_id"].nunique() funnel = pd.DataFrame( {"step": ["pv", "cart", "fav", "buy"], "users": [pv, cart, fav, buy]} ) funnel["rate"] = funnel["users"] / pv return funnel3.4 交互式看板(app.py)
import streamlit as st from metrics import daily_retention, conversion_funnel import matplotlib.pyplot as plt import seaborn as sns st.set_page_config(page_title="电商行为分析", layout="wide") st.title("电商用户行为分析 Demo") # 侧边栏参数 with st.sidebar: st.header("参数面板") day0 = st.date_input("基准日期", value=pd.to_datetime("2017-11-25")) span = st.slider("观察天数", 1, 14, 7) # 留存曲线 ret = daily_retention(day0.isoformat(), span) fig, ax = plt.subplots() sns.lineplot(x=ret.index, y=ret.values, marker="o", ax=ax) ax.set_title("N 日留存率") st.pyplot(fig) # 转化漏斗 funnel = conversion_funnel( pd.Timestamp("2017-11-25"), pd.Timestamp("2017-12-03") ) st.write("转化漏斗", funnel)本地一键启动:
streamlit run app.py浏览器打开http://localhost:8501,即可拖动日期、实时重算指标。
4. 代码规范:让评委愿意往下看
- 函数单一职责:
clean()只清洗,daily_retention()只算留存。 - 类型标注:全部加
-> pd.DataFrame等,Pyright 可静态检查。 - 路径用 pathlib:避免 Windows 与 Linux 斜杠混用。
- 魔法数字抽常量:如
max_ts提到模块顶部。 - 日志替代 print:引入
import logging; logger = logging.getLogger(__name__),方便后期切到文件日志。
5. 性能与安全:大数据量下不掉链子
- 分块读大文件:CSV 过 5G 时,用
pd.read_csv(..., iterator=True, chunksize=1_000_000)逐块清洗后追加到 Parquet。 - Streamlit 缓存:
@st.cache_data(ttl=3600) def daily_retention(...): ...避免每次交互都重算。
- 延迟加载:指标函数真正被调用时才读 Parquet,首页秒开。
- 防路径遍历:
from werkzeug.utils import secure_filename upload_name = secure_filename(file.name)确保用户无法../../../etc/passwd。
- 敏感列脱敏:
user_id展示时可用hashlib.sha256(str(uid).encode()).hexdigest()[:8],兼顾可追踪与隐私。
6. 生产环境避坑指南
依赖管理
用pip-compile生成锁定文件,Dockerfile 里COPY requirements.txt .先行安装,可缓存层加速。环境隔离
每分支配一条docker-compose.dev.yml,本地端口 8501,预发布 8502,互不影响。日志与监控
Streamlit 默认只打控制台;用logging.handlers.RotatingFileHandler写文件,再配 Promtail + Grafana 看板,服务器磁盘满早报警。云部署
- Streamlit Community Cloud:免费,但仓库需公开;适合答辩演示。
- 轻量服务器:1C2G 即可,用
tmux常驻会话;Nginx 反代 8501,配 HTTPS 证书,老师外网也能访问。 - 自动重启:写 systemd 服务文件
Restart=always,服务器重启自拉起。
CI/CD
GitHub Actions 里跑pytest + black + ruff,通过后才合并 main;保证每次 commit 都可跑通。
7. 可拓展方向:让评委看到“后续价值”
- 替换数据源:把 CSV 改成 MySQL binlog,用 SQLAlchemy 反射表,演示“近实时”指标。
- 增加预测模块:基于留存曲线,用 Prophet 或 LightGBM 预测未来 7 日活跃,把结果画成阴影区间,瞬间升级“预测性分析”。
- AB 实验看板:随机分流两组用户,前端按钮调参数,后端返回显著性检验 p 值,让系统具备“决策”能力。
- 引入缓存队列:Redis + Celery 把重指标异步计算,前端轮询进度条,工程分再 +10。
写在最后
整个流程跑下来,你会发现毕设不再只是“跑个图”,而是把数据、算法、接口、部署串成一条可交付的“产品”。先让系统能在云端跑通,再去丰富模型或加预测,心里会踏实得多。试试把上面的电商数据换成校园一卡通流水、共享单车的 GPS 记录,或任何你感兴趣的数据源,重复这套模板,很快就能得到一份“有技术、有展示、有故事”的毕业设计。祝你答辩顺利,代码常 Green!