尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

全栈实战:爬虫+Flask+随机森林+Vue构建医疗数据可视化大屏

全栈实战:爬虫+Flask+随机森林+Vue构建医疗数据可视化大屏 简介本资源是一套面向本科毕业设计与课程实践的医疗健康领域数据分析可视化系统融合Web全栈开发、机器学习建模与网络数据采集能力适用于计算机、生物信息或公共卫生相关专业学生开展综合项目实践。系统采用Flask构建后端API服务Vue实现响应式大屏前端集成requests爬虫自动获取疾病相关公开数据并基于随机森林算法完成疾病风险预测建模与特征重要性分析。压缩包共67个文件涵盖7个核心Python模块含爬虫、模型训练与Flask路由、7个Vue组件含ECharts动态图表、4个JSON配置与数据库脚本db_medicanlinfo.sql辅以配套论文、部署说明及3张运行效果截图整体仅1.32MB轻量易部署。目前已有43人学习下载提供从环境搭建、数据采集、模型训练到大屏渲染的完整闭环方案特别适合需要快速上手全栈AI融合项目的初学者与毕设学生。1. 项目概述与整体思路拆解先说结论这是一套典型的爬虫采集数据 Flask提供API 随机森林建模预测 Vue大屏呈现的全栈数据可视化项目也是目前医疗信息化、健康管理类毕业设计和个人作品集中最常见的组合形态。我之所以愿意花时间拆解这个标题是因为它几乎覆盖了Python生态里最常用的几条技术链路——数据从哪来、怎么存、怎么算、怎么展示一条龙全打通了。先把这个项目到底做了什么讲清楚。医疗疾病数据本身是一个很宽泛的概念常见的数据源包括卫健委公开的统计年鉴、各地疾控中心发布的传染病疫情通报、医院信息系统导出的门诊/住院病案首页数据以及一些公开的医学研究数据集。但在一个个人项目里我们通常拿不到真正的大规模临床数据所以更务实的做法是用requests爬虫去抓取公开可访问的疾病统计数据比如某地区逐月的流感、手足口病、乙肝等法定传染病发病人数存到本地数据库然后通过随机森林算法对哪些特征会影响发病率或未来一段时间发病率走势做预测建模最后把预测结果和历史数据一起丢到Vue大屏上展示。这个项目解决的核心问题有三个。第一医疗数据散落在各个网站和报告中非结构化程度高人工整理费时费力所以需要一个自动化的采集管道第二拿到数据之后光看表格看不出规律需要机器学习模型帮忙发现潜在关联并做预测第三预测结果如果只停留在控制台里价值就大打折扣必须做成可视化的、可用于汇报展示的形态。整条链路跑通之后你可以把它当成一个医疗健康数据的小型中台来用换数据源、换模型、换前端展示组件都相对容易。适合参考这个项目的人群也比较明确一是正在做毕业设计的计算机、大数据、医学信息工程专业学生这套技术栈匹配度高、工作量饱满二是想系统学习Flask后端开发、Vue前端工程化、sklearn机器学习实践的数据爱好者三是医疗信息化行业的从业者想快速搭一个数据展示原型给领导或客户看。但如果你对Python基础语法、Vue组件化开发、机器学习基本概念完全没有概念建议先把这三块的基础补一补再动手否则容易卡在环境配置上出不来。整个项目的技术选型我得说几句公道话。Flask Vue的组合对比Spring Boot React MySQL那一套最大的优势就是轻、快、好上手。Flask作为Python社区最经典的微框架没有Django那种全家桶的厚重感写一个RESTful API只需要几十行代码Vue则胜在渐进式你可以在一个HTML文件里用CDN方式先用起来再慢慢过渡到完整的vue-cli/webpack工程化结构。requests爬虫的选择也很务实医疗类公开数据网站大多没有太强的反爬机制requests加简单header伪装就能搞定完全不需要上scrapy那样重的框架。随机森林算法在医疗数据分析里算是非常稳妥的入门级选择它对数据分布要求低、不容易过拟合、还能输出特征重要性对非科班出身的开发者来说解释起来也比深度学习模型友好得多。这套系统跑起来之后大概长什么样左边是疾病发病率的地域或科室分布可以用ECharts地图或柱状图中间是核心的发病率趋势曲线和随机森林预测值曲线右边是关键指标卡片总病例数、最大单月增幅、预测上升/下降趋势、特征重要性排名、算法准确率等。顶部的日期筛选器、疾病类型下拉框可以触发后端接口的重新查询和预测。整体视觉是深色科技蓝风格这是大屏项目的行业惯例我后面会细说为什么建议选深色底。2. 数据采集层requests爬虫的实战细节爬虫是整个系统的数据源头也是最容易翻车的一环。很多人一上来就想着写一个通用爬虫去哪都能爬结果连一个网站的反爬都过不了。我的建议是先选定一个目标网站把数据字段确认清楚再动手写代码。对于医疗疾病数据有几个比较靠谱的方向地方卫健委官网定期发布的传染病防控数据、中国疾控中心公开的统计信息、一些医学信息平台整理的疫情历史数据库。2.1 目标网站分析与请求构造假设我们抓取的是某个地区卫健委每月发布的法定传染病发病情况。这类页面通常是列表页加详情页的结构列表页展示标题、发布日期详情页里才是具体数据表格。requests爬虫的第一步是模拟浏览器发起GET请求拿到HTML源码。这里有个重要的点不带headers直接请求大概率会被拒。服务器会检查User-Agent和Referer判断你是不是正常浏览器。我一般会构造一个看起来人畜无害的请求头import requests from bs4 import BeautifulSoup headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36, Referer: https://www.example.gov.cn/, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8 } resp requests.get(https://www.example.gov.cn/xxgk_xxgkml/yqxx/, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser)这里三个容易踩坑的点我单独说一下。第一编码问题很多政府网站是GBK或GB2312编码直接用resp.text解出来全是乱码。正确做法是先看resp.apparent_encoding再手动指定或者干脆用resp.content.decode(gbk, errorsignore)。第二超时设置requests.get不设timeout遇到慢网站会一直等爬虫像卡死一样。必须设置timeout参数并且建议配合retry机制。第三请求频率爬虫不是越快越好连续快速请求会给服务器造成压力也会提高被识别为爬虫的概率每抓一页最好sleep一个随机时间比如time.sleep(random.uniform(1, 3))。2.2 页面解析与数据结构化拿到HTML之后解析方式取决于页面结构。如果数据在table标签里BeautifulSoup find_all(tr)是最直接的办法如果页面是JavaScript动态渲染的这种情况在政府网站里也越来越常见了那requests只能拿到空壳HTML这时候有两种处理方案一是用selenium或playwright这类无头浏览器去渲染页面再解析二是按F12打开开发者工具找到数据接口的XHR请求直接请求那个JSON接口。我强烈推荐优先尝试第二种方案也就是抓接口而不是抓页面。原因很简单接口返回的一般是结构化JSON数据字段清晰不用费劲从HTML标签里抠数据而且接口请求比页面渲染快得多对服务器压力也更小。以一个典型的数据接口为例import requests import json # 这是开发者工具Network里找到的XHR请求 api_url https://www.example.gov.cn/api/Data/GetDiseaseList params { year: 2024, pageIndex: 1, pageSize: 20 } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, X-Requested-With: XMLHttpRequest, # 有些接口会校验这个头 Content-Type: application/json } resp requests.get(api_url, paramsparams, headersheaders, timeout10) data resp.json()解析完成之后数据长什么样以河南省或江苏省卫健委公开发布的数据为例一条记录通常包含疾病名称、发病数、死亡数、统计月份、地区、统计周期。这些字段直接可以作为我们的结构化数据来源。但公开数据有个特点——它经常是PDF或图片附件形式文字版本反而少。遇到这种情况要么人工把PDF转成Excel再导入要么用pdfplumber等库做表格抽取。我个人的建议是如果是个人项目抓取HTML表格或接口数据就够了不要把时间耗在PDF解析上性价比太低。2.3 数据清洗与持久化存储爬下来的原始数据基本都不能直接用。常见的问题有几千个疾病名称不一致手足口病和手足口并存、数据中有全角空格、数字列里混入了字符、相同记录重复抓取等等。我习惯建两个模块来处理一个是clean()函数做字段级别的清洗另一个是save_to_db()函数负责去重和入库。import pandas as pd from sqlalchemy import create_engine def clean_data(raw_rows): df pd.DataFrame(raw_rows) # 去掉完全重复的行 df df.drop_duplicates(subset[disease_name, region, stat_month]) # 数字列转为int去掉千分位逗号等干扰符 df[case_count] df[case_count].astype(str).str.replace(,, ).str.extract(r(\d)) df[death_count] df[death_count].astype(str).str.replace(,, ).str.extract(r(\d)) # 疾病名称统一表意 df[disease_name] df[disease_name].str.replace(手足口, 手足口病).str.strip() return df def save_to_db(df): engine create_engine(sqlite:///disease_data.db) df.to_sql(disease_records, engine, if_existsappend, indexFalse)存储层面个人项目用SQLite就够了零配置、单文件、好备份。但如果你想练手MySQL或PostgreSQL的部署和应用换连接字符串也就是一行代码的事。这里多说一句如果你计划在博文或毕设里展示自己的数据治理能力最好在清洗这个环节多写几个处理逻辑比如异常值检测、缺失值填充、单位统一这些都是面试官或答辩老师爱问的细节。3. Flask后端从API设计到机器学习模型接入数据爬下来之后后端要干的事情有两件把数据通过API吐给前端以及让随机森林模型跑起来做预测。Flask的灵活性在这里体现得很明显——你既可以用它写一个简单的数据查询服务也可以挂载机器学习模型做成预测服务两者之间几乎没有割裂感。3.1 Flask应用结构与API设计不建议把所有代码塞进一个app.py里项目再小也应该分模块。一个规范的Flask项目结构大概是这样的flask_backend/ ├── app.py # 应用入口注册蓝图 ├── config.py # 配置文件数据库连接、模型路径等 ├── models/ │ └── disease_model.py # 随机森林模型的训练和预测封装 ├── routes/ │ ├── __init__.py │ ├── data_api.py # 数据查询接口 │ └── predict_api.py # 预测接口 ├── utils/ │ └── db_helper.py # 数据库操作封装 ├── data/ │ └── disease_data.db # SQLite数据库 └── requirements.txt路由这块用Blueprint蓝图来做模块化一是代码结构清晰二是后续功能扩展时不用动主文件。先看主入口app.pyfrom flask import Flask from flask_cors import CORS from routes.data_api import data_bp from routes.predict_api import predict_bp app Flask(__name__) CORS(app) # 解决Vue开发服务器的跨域问题 app.register_blueprint(data_bp, url_prefix/api/data) app.register_blueprint(predict_bp, url_prefix/api/predict) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)这里有个容易忽略的细节为什么需要flask_cors因为Vue开发环境默认跑在8080端口Flask跑在5000端口浏览器里前端应用去请求后端接口属于跨域请求。如果你不在后端配置CORS前端控制台会直接报CORS error。生产部署如果前后端同域可以去掉这个配置但开发阶段建议保留。3.2 数据查询接口与前端联调数据查询接口负责给Vue大屏提供可视化所需的历史数据。接口路径可以设计成GET /api/data/disease_trend参数包括起始日期、结束日期、疾病名称、地区等。返回结构是JSON里面包含时间轴、各类疾病对应的发病率/发病人数。from flask import Blueprint, request, jsonify from utils.db_helper import query_db data_bp Blueprint(data_api, __name__) data_bp.route(/disease_trend, methods[GET]) def disease_trend(): start_date request.args.get(start, 2020-01) end_date request.args.get(end, 2024-12) disease request.args.get(disease, 手足口病) sql SELECT stat_month, disease_name, case_count, death_count FROM disease_records WHERE disease_name ? AND stat_month BETWEEN ? AND ? ORDER BY stat_month ASC rows query_db(sql, (disease, start_date, end_date)) result { disease: disease, trend: [ {month: r[stat_month], case_count: r[case_count], death_count: r[death_count]} for r in rows ] } return jsonify({code: 0, message: success, data: result})接口写完之后拿Postman或直接在浏览器里访问一下确认返回结果无误再进入前端联调阶段。很多新手容易在联调阶段栽跟头前端请求的URL写错了、后端返回的字段名对不上比如后端返回的是caseCount前端用的却是case_count、时间格式不统一。这些问题Debug起来非常耗时所以我建议前后端约定一个接口文档哪怕是简单的Markdown也把路径、参数、返回结构写清楚。3.3 随机森林模型训练与封装随机森林是我们的核心算法但说句实在话在这样一个大屏系统里模型并不需要做得多么复杂重点是能预测且预测结果能可视化。我推荐的预测思路是回归预测用过去N个月的发病率数据预测未来M个月的趋势。这里的关键是构造训练样本。假设我们有2015年到2024年共120个月的手足口病发病率数据现在想预测未来3个月的值。我们可以把数据做成滑动窗口的形式用前12个月的数据作为特征X预测第13个月的值作为标签y。每滑动一个月就生成一个新样本。这样120个月的数据大约能生成100多个训练样本对随机森林这种集成学习模型来说样本量虽然不大但做个趋势预测还是够用的。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split import numpy as np import joblib def prepare_features(series, window_size12): X, y [], [] for i in range(len(series) - window_size): X.append(series[i:i window_size]) y.append(series[i window_size]) return np.array(X), np.array(y) def train_model(case_counts): X, y prepare_features(case_counts) X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, shuffleFalse) model RandomForestRegressor( n_estimators200, max_depth8, min_samples_leaf2, random_state42 ) model.fit(X_train, y_train) # 保存模型文件后续预测时直接加载 joblib.dump(model, models/random_forest_model.pkl) return model, X_test, y_test随机森林的几个超参数我这里解释一下为什么这么设。n_estimators200代表森林里有200棵决策树树越多模型的稳定性越好但超过一定数量后收益递减200这个值在个人项目中性价比很高。max_depth8限制了每棵树的最大深度防止树过深导致过拟合尤其是我们训练样本量不大树太深几乎必然过拟合。min_samples_leaf2保证了叶子节点的最少样本数这也是一种正则化手段让模型不会死记硬背训练数据。训练完成后用测试集算一下R2分数和均方根误差。对医疗趋势预测模型R2能达到0.8以上就算不错了不用过于追求精确值。毕竟发病率受气候、卫生政策、人口流动等外部因素影响很大我们只用历史序列预测未来本身就存在信息瓶颈。3.4 预测结果的接口输出训练好的模型不能躺在磁盘里睡觉得通过API暴露给前端。预测接口的设计逻辑是前端传入要预测的疾病名称和未来月份数后端从数据库提取历史数据构造特征序列调用模型predict()方法返回预测结果。from flask import Blueprint, request, jsonify from utils.db_helper import query_db from models.disease_model import load_model, predict_future import numpy as np predict_bp Blueprint(predict_api, __name__) predict_bp.route(/, methods[POST]) def predict(): req_data request.get_json() disease req_data.get(disease, 手足口病) months_ahead int(req_data.get(months, 3)) # 从数据库获取该疾病的历史发病数据 rows query_db( SELECT stat_month, case_count FROM disease_records WHERE disease_name? ORDER BY stat_month, (disease,) ) if len(rows) 24: return jsonify({code: 1, message: 历史数据不足无法预测}) case_counts [r[case_count] for r in rows] model load_model(models/random_forest_model.pkl) # 用最后12个月数据作为起点滚动预测未来months_ahead个月 history case_counts[-12:] predictions [] for _ in range(months_ahead): next_val model.predict(np.array(history).reshape(1, -1))[0] predictions.append(round(float(next_val), 2)) history history[1:] [next_val] # 把预测值纳入历史继续滑动 return jsonify({ code: 0, data: { disease: disease, predictions: predictions, history: case_counts[-24:] # 方便前端把历史值和预测值画在同一张图上 } })这里有一个训练和预测时的关键点模型训练时用的是什么特征窗口预测时也必须使用相同的窗口长度。如果你训练时用12个月预测1个月那么预测接口里构造初始特征时也必须取最近12个月的数据。如果初始特征长度对不上模型会直接报维度错误。这类错误虽然不难修但排查起来容易让人烦躁我在实际开发中踩过不止一次。4. 前端Vue大屏可视化与交互实现前端是整个项目中观感最直观的部分也是答辩时最容易加分的部分。大屏可视化说到底是数据展示 视觉设计 交互操作三件事。我用Vue 3 Vite ECharts这套组合来搭Vue负责页面结构和组件化ECharts负责图表的绘制。选ECharts而不是Highcharts或D3.js原因很简单ECharts量大管饱地图、折线图、柱状图、饼图都有现成配置社区文档丰富遇到问题时一搜就有答案。4.1 大屏整体布局与自适应方案大屏项目的第一个难点不是写图表而是布局。因为大屏通常是通过HDMI或投屏方式放到LED屏或电视上展示的不同屏幕的分辨率差别很大从1080P到4K都有。如果布局写死了换到不同尺寸的屏幕就乱套。我采用的最稳妥的方案是固定设计稿 百分比缩放。先说思路设计时按1920x1080的尺寸做内容全部写在一个容器里然后用CSS的transform: scale()配合JavaScript动态计算缩放比例让容器自适应任何屏幕。这也是目前大屏项目最流行的自适应方案。简化后的实现思路template div classscreen-wrapper refscreenRef div classscreen-container :stylescreenStyle !-- 大屏内容 -- /div /div /template script export default { data() { return { screenStyle: { width: 1920px, height: 1080px, transform: scale(1), transformOrigin: left top } }; }, mounted() { this.resizeScreen(); window.addEventListener(resize, this.resizeScreen); }, beforeUnmount() { window.removeEventListener(resize, this.resizeScreen); }, methods: { resizeScreen() { const wrapper this.$refs.screenRef; const scaleX wrapper.clientWidth / 1920; const scaleY wrapper.clientHeight / 1080; const scale Math.min(scaleX, scaleY); this.screenStyle.transform scale(${scale}); } } }; /script注意几个细节外层wrapper需要设置为固定宽高比如100vw和100vh或者设成100%取决于父容器高度是否存在固定尺寸的容器必须在缩放后居中所以外层要加flex布局或者绝对定位。这个方案虽然代码量少但能解决90%的大屏适配问题实测下来很稳。4.2 用Vue组件化组织大屏区块大屏页面建议按区块拆分成Vue组件每个组件负责一个区域的内容。我习惯把大屏分成顶部标题区、左侧指标区、中间趋势区、右侧预测区、底部地图区这五块。组件拆分方式如下src/ ├── components/ │ ├── TopHeader.vue # 顶部标题栏 │ ├── LeftPanel.vue # 左侧指标卡片区 │ ├── CenterChart.vue # 中间历史发病率趋势图 │ ├── RightPredict.vue # 右侧随机森林预测图 │ ├── MapChart.vue # 底部地域分布地图 │ └── FilterBar.vue # 疾病类型、日期筛选栏组件间通信用Vue Pinia或简单的props/emit都行。大屏项目的数据流一般是这样的用户点击筛选器 - 筛选器组件emit事件给父组件 - 父组件调用后端API - 拿到数据后通过props分发给各个图表组件 - 图表组件watch props变化并更新ECharts。以中间趋势图为例核心代码大概是这样template div refchartRef classchart-container/div /template script import * as echarts from echarts; export default { props: { trendData: { type: Array, default: () [] } }, data() { return { chart: null }; }, mounted() { this.chart echarts.init(this.$refs.chartRef); this.renderChart(); }, watch: { trendData: { handler() { this.renderChart(); }, deep: true } }, methods: { renderChart() { if (!this.chart) return; const months this.trendData.map(item item.month); const cases this.trendData.map(item item.case_count); this.chart.setOption({ tooltip: { trigger: axis }, grid: { left: 3%, right: 4%, bottom: 3%, top: 8%, containLabel: true }, xAxis: { type: category, data: months, axisLabel: { color: #ccc } }, yAxis: { type: value, axisLabel: { color: #ccc } }, series: [{ name: 发病人数, type: line, smooth: true, symbol: circle, symbolSize: 6, itemStyle: { color: #00d4ff }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: rgba(0, 212, 255, 0.3) }, { offset: 1, color: rgba(0, 212, 255, 0) } ]) }, data: cases }] }); } } }; /script这里几个ECharts的配置心得分享一下。smooth: true让折线更柔和视觉上更大屏风areaStyle加上渐变色让趋势区域有层次感axisLabel颜色要配合深色背景选浅色否则看不清。大屏上图表不是越花哨越好反而线条简洁、配色统一更显专业。4.3 大屏视觉设计中的配色与交互细节大屏视觉设计我强烈建议采用深色底 高亮色的方案。原因有两个第一深色背景在LED屏和投影上表现更好不容易发白、反光第二荧光蓝、青色系的高亮数据在深色背景上对比度强远距离观看也清晰。我常用的配色是背景#0a1a2f或者#0b1a2e主色#00d4ff科技蓝辅助色#ff7c43警示橙、#00e396数据绿、#f7b500强调黄文字#e5e5e5。交互方面大屏系统一般不需要太多操作但至少要保留两个关键交互一是疾病类型切换二是时间范围选择。用下拉框还是按钮组不重要重点是切换后所有图表组件要同步更新。还有一个经常被忽略的细节数据为空时的状态处理。如果某个疾病在某个时间段没有数据图表不应该白屏至少要显示暂无数据的提示这个细节在答辩演示时能体现你的工程意识。5. 项目联调、常见问题与排错指南整个系统搭到能跑起来主要工作已经完成了80%剩下的20%是联调和排错。这一环节绝对是最磨人的但也是成长最快的地方。我把个人项目里高频踩坑的几个问题列出来方便你对照排查。5.1 跨域与端口配置问题现象一Vue通过axios请求http://localhost:5000/api/data/disease_trend浏览器控制台报CORS policy错误。原因前端和后端不同源。解决方案有三选后端启用flask_cors最简单加三行代码前端配置Vite代理在vite.config.js里设置server.proxy把/api开头的请求代理到http://localhost:5000这种方式生产环境不适用后端和前端部署在同一个域名下Nginx反向代理统一转发这是生产环境的正解。对于毕设项目我建议直接用flask_cors省事。如果你打算在作品集中展示工程化能力那就用Vite代理方案顺便能解释一下正向代理和反向代理的区别。5.2 随机森林模型维度不匹配现象二训练时模型还好好的部署到预测接口后调用predict时报X has 10 features, but RandomForestRegressor is expecting 12 features as input类似的错误。原因训练时的特征窗口是12个月但预测时你只传了10个数值。这个问题大概率出在数据预处理阶段历史数据不足12条或者清洗时把某些月份的数据过滤掉了。排查思路先看历史数据有没有缺月。比如你从数据库选的记录中间漏了某几个月或者最后一年的数据还没采集完整。解决办法有两种一是预测前做数据补全比如线性插值二是接口层面判断数据量不够就返回错误提示而不是强行预测。我在实际项目中更喜欢后者因为它更诚实地反映了数据质量问题。5.3 ECharts图表不渲染或空白现象三前端组件挂载了后端接口也能正常返回数据但图表区域一片空白。原因和排查步骤可以按顺序来确认DOM容器有高度。ECharts初始化时如果容器高度为0图表就不显示。大屏组件里经常出现父级flex布局子项没有显式高度导致图表容器塌陷的问题。确认是否在mounted之后才初始化。如果在data()阶段就初始化图表此时DOM还没渲染出来自然拿不到容器。检查数据是不是undefined。接口返回慢时组件可能先用空数据渲染了一次数据到达后需要watch触发重新setOption。如果没写watch图表就一直停留在空白状态。确认没有重复初始化。频繁进入页面或组件复用容易创建多个echarts实例内存上升且图表异常。建议在组件beforeUnmount里调用chart.dispose()释放实例注意keep-alive场景需要特殊处理。5.4 爬虫数据量少导致预测效果不佳现象四模型训练出来R2分数很低预测曲线几乎是平的或严重偏离真实值。原因数据量不够或者数据本身规律性弱。我这边验证过在只有两三年的月度数据情况下随机森林很难学到有意义的周期性。想缓解这个问题有几个可行方向尽量拉长数据时间范围从2015年甚至更早开始抓覆盖至少5年以上的月度数据加入更多特征比如把月份作为周期特征输入模型让模型知道哪个月是发病高峰改用SARIMA这类时间序列模型虽然超参数调起来麻烦点但对季节性数据的拟合能力比随机森林强。但说实话对于大屏展示场景模型精度并不是第一位的。你更需要的是一个看起来合理的预测曲线以及一套能讲清楚算法原理的PPT和答辩稿。随机森林在这一点上是够用的。6. 项目部署与扩展建议项目开发完成之后部署方式决定了它能不能稳定运行。我不建议把前后端放在本地跑着演示就完事了至少要学会用服务器部署才能算一个完整体验。部署方案我推荐两种一种是简单粗暴的云服务器部署另一种是Docker容器化。6.1 云服务器部署实操假设你有一台Linux云服务器2核4G配置足够部署流程如下安装Python 3.10、Node.js 18、MySQL或直接用SQLite把Flask后端代码上传到服务器git clone或scp创建虚拟环境pip install -r requirements.txt启动gunicorngunicorn -w 4 -b 0.0.0.0:5000 app:app前端代码执行npm run build生成dist目录用Nginx将静态文件指向dist目录同时将/api路径反向代理到127.0.0.1:5000配置Nginx的gzip压缩和缓存策略大屏加载速度会快很多。如果在部署过程中遇到端口访问不了的问题第一反应检查云服务商的安全组规则第二检查服务器防火墙第三看服务的监听地址是不是0.0.0.0而不是127.0.0.1。这三个排查完80%的部署问题都能解决。6.2 后续功能扩展方向系统跑通之后如果想继续进阶可以考虑这几个方向把requests爬虫改造成scrapy框架加上代理池和请求调度提升爬取效率和稳定性把历史数据从SQLite迁移到时序数据库或MySQL支持更大数据量的查询引入更多维度的数据温度、湿度、人口密度、医保支出等重新训练一个多特征随机森林模型预测准确率会有明显提升尝试用LSTM或Prophet替代随机森林做精度对比实验这也是毕设论文里很好写的创新点给大屏增加自动轮播和告警功能当预测发病率超过阈值时通过邮件或钉钉机器人发送通知。我在实际做过的项目里最推荐的扩展是多模型对比。同一个数据集随机森林、XGBoost、Prophet、LSTM各跑一遍输出一张误差对比表放到大屏的边角区域。这一块内容不仅视觉效果丰富还能让答辩老师看到你有模型选型的能力而不是只会调一个默认的随机森林API。7. 综合体验与最后的实用建议整套系统做下来我的体感是数据采集大概占20%的时间模型训练和调优占20%Flask后端API占15%Vue大屏前端的开发和调试占35%剩下的10%花在部署和联调上。这个比例说明项目真正的难点不在算法而在前端可视化的打磨上。很多人在做类似项目时把大部分精力放在训练模型上结果前端大屏丑得没法看这是本末倒置了。有几个具体的建议给到正在做或准备做这个项目的朋友第一数据源的合法性要留意。爬虫抓取的数据尽量选择公开、允许转载的政府统计数据或学术开放数据集不要爬取涉及个人隐私的医疗数据也不要违规使用商业平台的付费数据。项目展示时对数据来源做脱敏和说明这是工程素养的体现。第二模型训练和预测逻辑一定要写单元测试。哪怕只是简单的assert确保输入特征维度正确、输出是有限数值。这个习惯能帮你省去后端接口联调时的大量调试时间。我给这个小项目写过大约20个测试用例覆盖了数据清洗、模型预测、API返回三大块后面改需求时安全感高很多。第三大屏页面性能一定要注意。ECharts图表多的时候每次setOption全量更新会导致帧率下降。优化方案有两种一种是数据更新时只更新series.data不重复设置坐标轴和样式另一种是开启图表动画过渡ECharts自带的animationDuration配置就能让数据切换看起来流畅自然。最后再分享一个小技巧。大屏项目调试时不要只在电脑浏览器上按F12调可以打开浏览器开发者工具的设备模拟器切换到不同分辨率下看看布局有没有问题。有条件的话把页面投到电视或投影仪上真实验证一遍你会发现很多在电脑上发现不了的问题比如字体太小看不清、对比度不够、元素位置重叠等。这些细节恰恰是区分能跑的demo和能展示的作品的分界线。本文还有配套的精品资源点击获取
返回列表