又是一年毕设季后台收到不少私信都在问同一类问题数据分析和可视化方向的毕设到底怎么做才能不踩坑、又能让答辩老师满意。恰好我手头刚完成了一个“Flask京东香水XS数据分析与可视化”的项目从数据清洗、后端接口到可视化大屏、深度学习预测都走了一遍完整流程。这篇就把整个项目的设计思路、代码细节和答辩准备一并拆开讲无论你是正在选题还是已经开写都可以直接参考。先交代背景这是一个典型的“大数据深度学习Flask”三段式计算机毕设项目核心任务是抓取或获取京东商城香水品类的商品数据品牌、价格、销量、评价等用Flask搭建Web后端提供数据接口前端用ECharts渲染可视化大屏最后再用深度学习模型做销量趋势预测作为技术亮点。整套做完既能覆盖数据库、后端、前端、算法四个层面又能在答辩时拿出实打实的效果演示属于性价比很高的选题方向。下面按我的实际开发顺序把每个环节的关键点都过一遍。1. 选题定位为什么是Flask搭配香水数据1.1 毕设选题的“稳妥”与“亮点”权衡每年选题季最怕的就是选一个题目看起来高大上、做起来处处是坑的课题。大数据和深度学习是热门标签但如果直接上手分布式集群加模型训练光是环境配置就能耗掉大半时间。我这套方案选Flask核心原因在于它是一个轻量级Web框架起步快、文档全、对毕设场景足够用同时又能完整地串起“数据获取—数据处理—接口服务—前端展示”整条链路。Flask在这类项目里的定位不是去和Django比功能齐全而是用最少的代码把后端服务跑起来。比如你只需要提供一个JSON接口给前端大屏拉数据Flask几行代码就能搞定不用像Django那样先处理一堆默认配置。对毕设来说重点在于你能否把“为什么这么选”讲清楚而不是工具本身有多庞大。1.2 香水数据的分析价值在哪里选京东香水XS这个数据集看起来是个垂直品类但分析维度非常丰富。香水的品牌分布、价格区间、销量排行、用户评价、香调偏好都能映射出消费行为规律。比如可以分析“哪些价位的香水销量最高”“大牌香水和小众香水的评价量差异”“不同香调在不同价格段的分布”这些问题既好理解又容易出可视化效果答辩时也很容易讲出业务含义。数据规模也不需要太大几千到一万条有效记录就足够支撑整个毕设的分析和建模需求。相比动辄上百万条的数据集小规模数据让清洗和建模的周期大大缩短但分析逻辑和工程流程却是完整的这才是毕设真正要训练的能力。2. 数据从哪来京东香水XS数据集的结构化处理2.1 数据字段设计与存储表结构拿到原始数据后第一件事不是急着分析而是把它整理成结构化表格。我设计的核心字段包括商品ID、商品名称、品牌、价格、累计销量、评价数、好评率、上架时间、香调分类、规格容量。这些字段基本覆盖了后续所有分析角度。MySQL里建表时我用的核心DDL大致是这样的CREATE TABLE perfume ( id INT PRIMARY KEY AUTO_INCREMENT, product_id VARCHAR(32) UNIQUE, product_name VARCHAR(128), brand VARCHAR(64), price DECIMAL(10,2), sales_volume INT, review_count INT, positive_rate DECIMAL(5,2), launch_date DATE, fragrance_type VARCHAR(32), capacity VARCHAR(16) );这里有个容易被忽视的细节product_id一定要加唯一约束。京东的商品数据在不同时间抓取会重复如果没有唯一键后续去重会非常痛苦。而price用DECIMAL不用FLOAT是为了避免浮点数精度导致的价格展示误差。2.2 清洗过程的三个关键步骤数据清洗是答辩时老师最爱问的部分一定要能讲清逻辑。我实际处理时主要做了三件事去重、缺失值处理、异常值处理。去重逻辑很简单按product_id分组保留最新一条即可用SQL就能完成DELETE p1 FROM perfume p1 INNER JOIN perfume p2 WHERE p1.id p2.id AND p1.product_id p2.product_id;缺失值处理分两种情况一是价格或销量为空这类记录影响分析结果直接删除二是香调分类为空考虑到香调是后续分析的重要维度我选择按品牌已有的其他商品香调做众数填充而不是直接删除。异常值才是重头戏。我遇到比较典型的情况是有些香水商品标价0.01元实际是赠品或凑单链接还有个别商品的销量超过10万但评论数几乎为0刷单嫌疑。对这类数据我设定了一个过滤规则价格低于1元的剔除销量超过均值三倍标准差的标记为异常并人工复核。这里可以用pandas快速完成import pandas as pd df pd.read_csv(perfume_raw.csv) df df[df[price] 1] mean_sales df[sales_volume].mean() std_sales df[sales_volume].std() df df[df[sales_volume] mean_sales 3 * std_sales]清洗完的数据我做了一张“前后对比表”这个习惯在答辩时挺加分原始数据多少条、清洗后多少条、各类占比如何一目了然。2.3 从CSV到MySQL的导入方案数据量不大时用Python的pandasSQLAlchemy导入效率很高from sqlalchemy import create_engine engine create_engine(mysqlpymysql://root:passwordlocalhost:3306/perfume_db) df.to_sql(perfume, conengine, if_existsreplace, indexFalse)这里我把编码统一设置为utf8mb4不然导入商品名称里带特殊符号时会报错。另外to_sql默认不走批量提交数据量到十万条以上会非常慢但香水量级完全没压力。3. Flask后端把数据库变成前端能用的接口3.1 项目目录和蓝图划分Flask项目我最常用的目录结构如下perfume_project/ ├── app.py ├── blueprints/ │ ├── __init__.py │ ├── analysis.py │ └── predict.py ├── models/ │ └── database.py ├── static/ │ └── echarts/ ├── templates/ │ └── dashboard.html └── requirements.txt用蓝图Blueprint的好处在于分析接口和预测接口可以分开维护。毕设项目虽然不大但代码组织得清晰答辩时老师翻项目文件也会有好的第一印象。3.2 核心接口设计与实现整套系统我设计了5个核心接口对应大屏上的不同模块/api/overview汇总卡片数据总商品数、品牌总数、平均价格、总销量/api/brand_rank品牌销量Top10/api/price_distribution价格区间分布/api/fragrance_radar香调维度的多指标雷达图数据/api/trend近12个月销量趋势每个接口的返回值统一用{code: 200, data: ..., msg: success}格式。这样做的好处是前端联调时不用针对每个接口单独处理异常结构。一个典型的品牌销量Top10接口实现如下from flask import Blueprint, jsonify from models.database import db analysis_bp Blueprint(analysis, __name__) analysis_bp.route(/api/brand_rank) def brand_rank(): sql SELECT brand, SUM(sales_volume) AS total_sales FROM perfume GROUP BY brand ORDER BY total_sales DESC LIMIT 10 result db.session.execute(sql).fetchall() data [{brand: row[0], sales: int(row[1])} for row in result] return jsonify({code: 200, data: data, msg: success})这里我用的是SQLAlchemy的session.execute直接跑原生SQL省去了写ORM模型的繁琐。数据量不大时这种方式的执行效率和代码量都比ORM更友好。3.3 联调过程中容易忽略的跨域问题如果你的Flask页面本身就在templates里由Flask直接渲染那不存在跨域问题。但我当时为了调试方便把前端大屏独立运行在开发服务器上这时候就遇到跨域报错。解决办法很简单给Flask加个flask-cors扩展from flask_cors import CORS CORS(app)或者手动在请求头里加上Access-Control-Allow-Origin: *。这个坑其实很小但不少人在联调阶段被卡住值得提醒一下。4. 可视化大屏ECharts图表的选型与布局4.1 大屏布局的网格思路可视化大屏做的是16:9的展示页面我采用三列布局左侧放价格分布和品牌Top10中间放核心指标卡片加销量趋势右侧放香调雷达图和热力图。这种布局符合阅读动线核心数据在中间辅助分析在两侧。CSS Grid是最高效的实现方式div classdashboard div classleft-column.../div div classcenter-column.../div div classright-column.../div /div.dashboard { display: grid; grid-template-columns: 1fr 1.2fr 1fr; gap: 16px; height: 100vh; padding: 16px; background: #0f1c3a; }深色背景配高亮颜色是数据大屏的经典配色。我在每个图表外面套了一层半透明边框视觉上会显得整体更精致这个是CSS的border-radius和box-shadow实现的不需要额外UI库。4.2 图表选择背后的数据逻辑图表不是越多越好而是要跟数据维度匹配。我的选型逻辑是品牌销量对比用横向条形图品牌名较长时横向比纵向更易读价格区间分布用饼图体现占比结构香调分析用雷达图展示“花香”“木质调”“东方调”“果香”“清新调”五个维度的综合表现近12个月销量趋势用折线图直观反映波动和季节性评论区关键词用词云图体现用户关注点其中雷达图的数据格式比较特殊需要提供多个指标维度我在后端接口里直接聚合好各项数值前端只用一次性渲染。下面是一个关键代码片段$.ajax({ url: /api/fragrance_radar, method: GET, success: function(res) { const data res.data; chart.setOption({ radar: { indicator: data.indicators }, series: [{ type: radar, data: [{ value: data.values, name: 香调综合评分 }] }] }); } });4.3 图表自适应的常见处理大屏在不同分辨率的显示器上显示效果差异很大。我的做法是用rem配合视口单位同时在CSS里加一个缩放逻辑function resizePage() { const scaleX window.innerWidth / 1920; const scaleY window.innerHeight / 1080; const scale Math.min(scaleX, scaleY); document.querySelector(.dashboard).style.transform scale(${scale}); } window.addEventListener(resize, resizePage);这个办法比较粗暴但好用能保证在答辩现场任何一台电脑上打开页面都不会布局错乱。ECharts本身也有resize方法在窗口尺寸变化时记得调用一下所有图表的resize()。5. 深度学习预测销量趋势的前瞻模块5.1 为什么在毕设里加深度学习如果只是做“查询展示”这套系统本质上是一个BI看板技术含量容易被低估。我在系统中加入销量预测模块一方面是想让项目的技术层次从“统计分析”升级为“预测分析”另一方面也是考虑到“大数据深度学习”这个标题标签需要在正文里有真实支撑。预测目标是未来7天的香水总销量。数据集里的销量数据是按月度汇总的周期较短我把它整理成按天统计的时间序列再用LSTM做序列预测。5.2 数据准备与模型训练LSTM对数据量有一定要求但香水的销量数据并不大为了防止过拟合我采用了滑动窗口构造样本用过去14天的销量预测未来1天的销量。数据标准化用MinMaxScaler缩放到[0,1]区间。核心代码from keras.models import Sequential from keras.layers import LSTM, Dense import numpy as np def create_sequences(data, seq_len14): X, y [], [] for i in range(len(data) - seq_len): X.append(data[i:iseq_len]) y.append(data[iseq_len]) return np.array(X), np.array(y) model Sequential() model.add(LSTM(50, activationrelu, input_shape(14, 1))) model.add(Dense(1)) model.compile(optimizeradam, lossmse) model.fit(X_train, y_train, epochs50, batch_size8, validation_split0.2)LSTM的参数设置上我用了单层50个神经元原因是样本量不算大层数太多容易过拟合。损失函数选mse因为它能较好地对连续销量值做回归预测。训练完保存模型再在Flask里加载预测接口直接调model.predict即可from keras.models import load_model model load_model(models/lstm_sales.h5) analysis_bp.route(/api/predict_week) def predict_week(): last_14_days get_last_14_days() pred model.predict(last_14_days.reshape(1, 14, 1)) return jsonify({code: 200, data: float(pred[0][0])})5.3 深度学习模块在答辩中的讲法答辩时老师最容易追问的是“为什么选择LSTM而不是其他模型”。我的回答思路是销量预测属于时间序列预测问题LSTM本身适合捕捉序列中的长期依赖关系相比ARIMA等传统统计模型它对非线性模式有更强的拟合能力。同时我会补一句在样本量不大的前提下LSTM的有效性需要结合实验验证来评估所以我在项目里对ARIMA和LSTM都做了对比实验最终LSTM的测试集RMSE更小。这么一说既展示了思考深度又避开了“深度学习一定更好”的绝对化表述。6. 答辩现场演示流程和常见问题应对6.1 演示的黄金五分钟答辩演示不需要把每个模块都讲一遍重点是展示“你做了什么”和“为什么这么做”。我的演示顺序是先花一分钟展示大屏整体效果再用两分钟演示两个核心分析维度品牌销量Top10和价格分布接着用一分钟展示销量预测结果最后留一分钟讲项目亮点和可扩展方向。整套演示控制在5分钟以内剩下时间留给老师提问。大数据屏展示环节有个细节提前把要用到的页面在本地浏览器打开并调好窗口大小不要在现场临时输入网址加载避免网络或环境问题。我用的是本地IP地址访问这样就算现场断网也能正常演示。6.2 六个高频问题和参考应答这里的回答话术我按“先讲思路、再讲操作、最后补依据”的原则整理Q1数据是怎么获取的顺着数据集来源说明清楚就行如果是自己抓的就说自己写了爬虫如果是公开数据集就说明数据清洗和预处理流程。重点是强调你对数据的理解而不是获取方式本身。Q2数据清洗做了哪些工作去重、缺失值处理、异常值检测三块分开讲再举一两个具体的例子比如价格小于1元的记录如何处理、三个标准差原则怎么用。Q3为什么用Flask而不用DjangoFlask轻量灵活适合快速搭建API服务对小型数据分析系统开发效率高Django功能完善但重适合大型应用。毕设项目规模有限选Flask能把更多精力放在分析和可视化上。Q4LSTM模型训练的参数怎么确定神经元数量、窗口长度、训练轮数都做了对比实验可以列一个简单的实验结果表格说明不同参数对应的RMSE值。这样既显得严谨也说明参数不是随便拍的。Q5系统的性能瓶颈在哪里现在数据量小性能瓶颈不明显但如果数据量扩大主要瓶颈会在数据库查询效率和ECharts渲染大量节点的开销上。可扩展方案是引入Redis做缓存、用异步任务处理大数据量聚合查询。Q6这个项目还能怎么扩展可以增加用户登录和权限管理可以对接实时数据源做自动更新也可以把预测模型换成Transformer架构提升精度。顺便提一句“可以从Excel导入自定义数据”这句话能引出不少业务想象空间。6.3 技术之外的重要提醒有一类问题容易被忽略老师可能会问“数据和结论是否可靠”。比如你分析出某品牌销量最高但样本里是不是存在数据偏差我的做法是在分析模块里加上一条数据声明说明数据的采集时间和样本范围并对明显的抽样偏差做文字标注。这样即使结论有局限也体现出了你具备基本的数理素养。7. 从毕设到个人项目的延伸思考整套做下来我最大的感受是毕设项目与其追求大而全不如做到小而透。Flask加ECharts加LSTM这套组合放在工业界或许不够看但它完整覆盖了数据采集、清洗、存储、接口、可视化、建模、部署全流程比单纯写一个算法模型或者单纯做一个页面能学到的多得多。如果你已经在做类似项目我也建议额外留一点时间把项目里关键的决策记录下来。因为答辩时要的不是“我写了很多代码”而是“我知道每一步为什么这样设计”。我看答辩现场很多同学被问住不是因为功能没实现而是因为答不出设计理由。哪怕你自己赶工写的代码逻辑梳理清楚之后表达出来的感觉是完全不一样的。最后分享一个非常实用的小技巧准备一张“项目技术架构图”放在答辩PPT里但不要画得太复杂就用最简单的方框层级表示“数据层—服务层—展示层—算法层”每层标上实际用到的技术。这张图就是你的回答提纲任何问题都可以沿着它往下讲。我答辩那会儿好几个问题都靠这张图稳住了节奏。