新闻详情

新闻详情

首页 / 资讯中心 / 详情

Python旅游推荐数据分析可视化毕业设计实战:从数据清洗到ECharts大屏

发布时间:2026/10/1 17:35:16来源:尧图网络
Python旅游推荐数据分析可视化毕业设计实战:从数据清洗到ECharts大屏
简介这份资源是面向计算机相关专业毕设学生与Python实战学习者的旅游推荐数据分析可视化项目采用pythonDjangomysql技术栈并引入协同过滤算法实现个性化推荐可直接作为毕业设计、课程设计或期末大作业使用。压缩包共422个文件约9.36MB以174个js、59个css、40个json、27个py、18个html等为主涵盖前端页面样式、后端业务逻辑、数据配置与模板渲染另附sql数据库脚本、docx部署说明及md文档结构完整便于二次开发。项目功能覆盖用户登录注册、个人信息管理、景区浏览、评分收藏与智能推荐管理员端支持景区信息及分类的增删改查、用户资料与行为记录管理。已有82人学习适合需要完整赛题方案、可运行源码与部署参考的读者快速上手并完成答辩准备。1. 旅游推荐数据分析可视化从一份毕业设计源码能跑出什么很多同学拿到「基于 Python 的旅游推荐数据分析可视化」这个题目时第一反应是去搜免费 Python 源码大全下载一个压缩包解压然后卡在第一步——跑不起来。我见过太多这样的场景源码里requirements.txt列了三十多个包Python 版本不对数据库连不上前端页面白屏最后只能截图交差。这个方向本身是有价值的它把 Python 数据分析、推荐算法、ECharts 可视化大屏串成了一条完整的链路既能体现数据处理能力又能展示工程落地。适合谁适合计算机相关专业做毕业设计的学生也适合想入门数据分析项目实战的开发者。核心要解决的问题就三个数据从哪来、推荐怎么算、图表怎么画。把这三件事拆开每一步都能复现这个项目就不只是交差而是能写进简历的东西。2. 数据层旅游数据从哪来、怎么存、怎么清洗2.1 数据来源的三种现实选择做旅游推荐数据是地基。常见做法有三种第一种是公开数据集比如携程、去哪儿等平台的历史评论数据或者一些数据竞赛平台上的旅游景点数据集字段通常包含景点名称、城市、评分、评论数、门票价格、标签等第二种是自己写爬虫抓取用requestsBeautifulSoup或Scrapy目标站点选结构清晰的旅游攻略页面注意控制频率、遵守 robots 协议第三种是模拟生成用Faker库造一批看起来合理的数据适合时间紧、只演示流程的情况。我一般会建议如果导师不要求真实数据用公开数据集最稳省去反爬和清洗的麻烦如果要求真实就选一个页面结构简单的站点抓个几千条就够用了。数据存哪小规模用 SQLite 就够了零配置一个文件搞定Python 内置支持。规模大一点或者想练手 MySQL就装一个本地 MySQL用pymysql或SQLAlchemy连接。Redis 在这个项目里不是必须的但如果你想做热门景点缓存或者推荐结果缓存可以加一个 Redis 可视化客户端来观察键值这算加分项。2.2 建表与数据清洗的实操代码假设我们抓到的原始数据是一份 CSV字段有景点名称、城市、评分、评论数、价格、标签、简介。先建表再清洗。下面这段代码可以直接抄import pandas as pd import sqlite3 # 读取原始 CSV注意编码中文数据常见 utf-8 或 gbk df pd.read_csv(travel_raw.csv, encodingutf-8) # 查看缺失情况 print(df.isnull().sum()) # 清洗评分转为数值缺失值用中位数填充 df[评分] pd.to_numeric(df[评分], errorscoerce) df[评分].fillna(df[评分].median(), inplaceTrue) # 评论数转为整数缺失填 0 df[评论数] pd.to_numeric(df[评论数], errorscoerce).fillna(0).astype(int) # 价格去掉“元”字转为浮点数 df[价格] df[价格].astype(str).str.replace(元, , regexFalse) df[价格] pd.to_numeric(df[价格], errorscoerce) df[价格].fillna(df[价格].median(), inplaceTrue) # 标签列拆分取第一个标签作为主分类 df[主标签] df[标签].astype(str).str.split(,).str[0] # 去重按景点名称去重保留评论数最多的那条 df df.sort_values(评论数, ascendingFalse).drop_duplicates(subset[景点名称]) # 写入 SQLite conn sqlite3.connect(travel.db) df.to_sql(scenic, conn, if_existsreplace, indexFalse) conn.close() print(清洗完成共, len(df), 条记录)逻辑说明pd.to_numeric的errorscoerce参数会把无法转换的值变成 NaN避免直接报错。fillna用中位数而不是均值是因为旅游数据里价格和评分常有极端值中位数更稳。去重时按评论数排序再保留是为了留下信息量最大的记录。参数方面encoding要根据实际文件调整如果报UnicodeDecodeError换成gbk或gb18030再试。2.3 数据质量检查的三个关键指标清洗完不是就完了得验证。我一般看三个指标一是缺失率每个字段缺失超过 30% 就要考虑是否保留该字段二是异常值比如评分出现 100 分、价格为负数这些要过滤掉三是分布合理性用df.describe()看价格和评分的分位数如果 75% 分位数和最大值差距过大说明有极端值拉偏了。下面这段检查代码可以放在清洗之后# 缺失率检查 missing_rate df.isnull().sum() / len(df) print(缺失率超过 30% 的字段) print(missing_rate[missing_rate 0.3]) # 异常值过滤 df df[(df[评分] 0) (df[评分] 5)] df df[df[价格] 0] # 分布查看 print(df[[评分, 评论数, 价格]].describe())这一步做完数据层就算稳了。很多毕业设计翻车就翻在数据没洗干净后面推荐算出来的结果全是错的图表也难看。3. 推荐算法协同过滤和基于内容的推荐怎么选、怎么调3.1 两种推荐思路的适用场景旅游推荐常见的算法就两类协同过滤和基于内容的推荐。协同过滤需要用户-物品交互数据比如用户对景点的评分或浏览记录。如果你手头只有景点信息没有用户行为那就只能用基于内容的推荐靠景点标签、城市、价格区间做相似度匹配。我一般会建议如果数据里有用户 ID 和评分优先做协同过滤效果更直观如果没有就用基于内容的推荐用 TF-IDF 把标签向量化再算余弦相似度。协同过滤又分 UserCF 和 ItemCF。UserCF 是找相似用户推荐他们喜欢的景点ItemCF 是找相似景点推荐给看过当前景点的人。旅游场景下 ItemCF 更稳定因为景点之间的相似性比用户之间的相似性更容易解释。下面重点讲 ItemCF 的实现。3.2 ItemCF 的完整实现与参数调优假设我们有用户-景点评分矩阵用surprise库或者手写都可以。手写更可控适合毕业设计展示原理。核心步骤构建共现矩阵、计算相似度、生成推荐列表。import pandas as pd import numpy as np from sklearn.metrics.pairwise import cosine_similarity # 假设 ratings 是 DataFrame列user_id, scenic_id, rating # 构建用户-景点评分矩阵 matrix ratings.pivot_table(indexuser_id, columnsscenic_id, valuesrating).fillna(0) # 计算景点之间的余弦相似度 sim cosine_similarity(matrix.T) sim_df pd.DataFrame(sim, indexmatrix.columns, columnsmatrix.columns) # 给某个用户推荐找他已经评过分的景点加权相似度求和 def recommend(user_id, top_n10): user_ratings matrix.loc[user_id] rated user_ratings[user_ratings 0].index.tolist() scores {} for scenic in rated: similar sim_df[scenic].drop(indexrated, errorsignore) for s, score in similar.items(): scores[s] scores.get(s, 0) score * user_ratings[scenic] # 排序取前 top_n return sorted(scores.items(), keylambda x: x[1], reverseTrue)[:top_n] print(recommend(1))逻辑说明pivot_table把长表转成矩阵缺失值填 0 表示未评分。cosine_similarity计算列与列之间的相似度即景点与景点的相似度。推荐时用用户已有评分加权相似度评分越高的景点对推荐结果影响越大。参数方面top_n控制推荐数量一般 10 到 20 比较合适相似度阈值可以加一个过滤比如只保留相似度大于 0.2 的避免噪声。如果数据稀疏可以先用TruncatedSVD降维再算相似度效果会好一些。另外冷启动问题在旅游场景很常见新用户没有评分这时候就退化成基于内容的推荐用热门景点或高评分景点兜底。3.3 基于内容的推荐TF-IDF 加余弦相似度没有用户行为数据时基于内容的推荐是唯一选择。把每个景点的标签、城市、简介拼成一个文档用 TF-IDF 向量化再算相似度。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import linear_kernel # 拼接特征文本 df[特征] df[城市] df[主标签] df[简介].fillna() # TF-IDF 向量化 tfidf TfidfVectorizer(stop_wordsNone, max_features5000) tfidf_matrix tfidf.fit_transform(df[特征]) # 计算余弦相似度 cosine_sim linear_kernel(tfidf_matrix, tfidf_matrix) # 根据景点名称推荐相似景点 indices pd.Series(df.index, indexdf[景点名称]).drop_duplicates() def content_recommend(name, top_n10): idx indices[name] sim_scores list(enumerate(cosine_sim[idx])) sim_scores sorted(sim_scores, keylambda x: x[1], reverseTrue)[1:top_n1] scenic_indices [i[0] for i in sim_scores] return df[景点名称].iloc[scenic_indices] print(content_recommend(西湖))逻辑说明TfidfVectorizer的max_features控制词汇表大小5000 对于几千条数据够用了。linear_kernel比cosine_similarity快因为 TF-IDF 向量已经归一化。indices用来把景点名称映射到行号注意drop_duplicates防止同名景点报错。参数方面stop_words中文需要自己提供停用词表或者用jieba分词后再传入。4. 可视化用 ECharts 和 Flask 搭一个能看的数据大屏4.1 后端 Flask 接口设计可视化大屏的数据不能写死在前端得从后端接口拿。用 Flask 写几个简单的 API返回 JSON 给 ECharts 用。常见接口热门城市 Top10、评分分布、价格区间分布、推荐结果列表。from flask import Flask, jsonify, render_template import sqlite3 import pandas as pd app Flask(__name__) def query_db(sql): conn sqlite3.connect(travel.db) df pd.read_sql(sql, conn) conn.close() return df app.route(/) def index(): return render_template(dashboard.html) app.route(/api/city_top10) def city_top10(): df query_db(SELECT 城市, COUNT(*) as 数量 FROM scenic GROUP BY 城市 ORDER BY 数量 DESC LIMIT 10) return jsonify(df.to_dict(orientrecords)) app.route(/api/price_dist) def price_dist(): df query_db(SELECT 价格 FROM scenic) bins [0, 50, 100, 200, 500, 1000, 99999] labels [0-50, 50-100, 100-200, 200-500, 500-1000, 1000] df[区间] pd.cut(df[价格], binsbins, labelslabels) result df.groupby(区间).size().reset_index(name数量) return jsonify(result.to_dict(orientrecords)) if __name__ __main__: app.run(debugTrue, port5000)逻辑说明query_db封装了连接和查询避免重复代码。pd.cut做分箱把连续价格转成区间。jsonify把 DataFrame 转成 JSON 数组前端直接消费。参数方面port默认 5000如果被占用改成 5001debugTrue开发时用部署时关掉。4.2 ECharts 大屏的四个核心图表前端用 ECharts从 CDN 引入不用 npm 打包简单直接。四个图表柱状图展示热门城市、饼图展示价格区间、折线图展示评分趋势、表格展示推荐结果。!DOCTYPE html html head meta charsetutf-8 title旅游数据大屏/title script srchttps://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js/script /head body div idcityChart stylewidth: 600px; height: 400px;/div div idpriceChart stylewidth: 600px; height: 400px;/div script // 热门城市柱状图 fetch(/api/city_top10).then(r r.json()).then(data { const chart echarts.init(document.getElementById(cityChart)); chart.setOption({ title: { text: 热门城市 Top10 }, xAxis: { type: category, data: data.map(d d.城市) }, yAxis: { type: value }, series: [{ type: bar, data: data.map(d d.数量) }] }); }); // 价格区间饼图 fetch(/api/price_dist).then(r r.json()).then(data { const chart echarts.init(document.getElementById(priceChart)); chart.setOption({ title: { text: 价格区间分布 }, series: [{ type: pie, data: data.map(d ({ name: d.区间, value: d.数量 })) }] }); }); /script /body /html逻辑说明fetch请求后端接口拿到 JSON 后直接喂给 ECharts 的setOption。map做数据转换把对象数组转成 ECharts 需要的格式。参数方面echarts.init的容器必须有明确宽高否则图表不显示。CDN 地址用 jsdelivr国内访问一般没问题如果加载慢可以换成本地文件。4.3 大屏布局与响应式适配毕业设计的大屏通常要求 1920x1080 分辨率下好看但答辩时可能用笔记本所以得做响应式。简单做法用 flex 布局图表容器宽度用百分比echarts.init后监听resize事件。window.addEventListener(resize, () { chart.resize(); });如果要做成真正的数据大屏可以加一个深色背景、标题栏、时间显示用 CSS grid 把图表排成三列。注意 ECharts 的resize要每个实例都调用或者用echarts.connect统一管理。5. 避坑与排查部署时最容易翻车的五个地方5.1 现象ModuleNotFoundError: No module named xxx原因依赖没装全或者 Python 环境不对。源码里的requirements.txt可能不完整或者你用的 Python 版本和作者不一致。解决先pip install -r requirements.txt如果还报错手动装缺失的包。建议用虚拟环境python -m venv venv激活后再装避免污染全局。Python 版本尽量用 3.8 到 3.10太新的版本有些包不兼容。5.2 现象Flask 启动后访问 500日志显示 SQLite 找不到表原因数据库文件路径不对或者表没建。sqlite3.connect(travel.db)是相对路径如果从不同目录启动 Flask就会找不到文件。解决用绝对路径os.path.join(os.path.dirname(__file__), travel.db)。另外确保清洗脚本先跑过表已经存在。5.3 现象ECharts 图表空白控制台报Cannot read property init of undefined原因ECharts 的 CDN 没加载成功或者echarts.init的容器 ID 写错了。解决检查浏览器控制台 Network 面板看 echarts.min.js 是否 200。如果 CDN 被墙下载到本地放到static目录。容器 ID 要和getElementById一致大小写敏感。5.4 现象推荐结果全是同一个景点原因相似度矩阵计算有问题或者数据里某个景点出现次数太多导致它和所有景点都相似。解决检查cosine_similarity的输入矩阵确保没有全零行。如果是基于内容的推荐检查 TF-IDF 的max_features是否太小导致特征区分度不够。可以加一个惩罚项对热门景点降权。5.5 现象中文乱码图表上显示方框原因Flask 返回的 JSON 默认ensure_asciiTrue中文被转义了。解决app.config[JSON_AS_ASCII] False或者用jsonify时加ensure_asciiFalse。另外HTML 的meta charsetutf-8要加上数据库连接也可以指定charsetutf8。6. 进阶技巧把推荐结果做成可解释的卡片最后一章说一个让毕业设计加分的小技巧推荐结果不要只给一个列表做成带解释的卡片。比如「推荐西湖因为你去过千岛湖两者都是湖泊类景点评分都在 4.5 以上」。实现方式是在推荐函数里同时返回相似度最高的那个已评分景点和相似原因。def recommend_with_reason(user_id, top_n5): user_ratings matrix.loc[user_id] rated user_ratings[user_ratings 0].index.tolist() scores {} reasons {} for scenic in rated: similar sim_df[scenic].drop(indexrated, errorsignore) for s, score in similar.items(): scores[s] scores.get(s, 0) score * user_ratings[scenic] if s not in reasons or score reasons[s][1]: reasons[s] (scenic, score) result sorted(scores.items(), keylambda x: x[1], reverseTrue)[:top_n] return [{景点: s, 得分: round(v, 2), 因为你去过: reasons[s][0], 相似度: round(reasons[s][1], 2)} for s, v in result]逻辑说明reasons字典记录每个推荐景点最相似的已评分景点和相似度。返回时组装成字典列表前端可以直接渲染成卡片。参数方面top_n控制卡片数量5 到 8 个比较合适太多页面放不下。验证方法找几个测试用户人工看推荐结果是否合理。如果推荐结果和已评分景点完全不相关说明相似度计算有问题回去检查 TF-IDF 或共现矩阵。我一般会抽 10 个用户每个看前 5 个推荐命中率超过 60% 就算及格。最后说个血泪经验毕业设计答辩时老师最常问的是「你这个推荐和直接按评分排序有什么区别」。所以一定要准备一个对比实验比如随机推荐、热门推荐、协同过滤推荐三组算一下准确率或召回率哪怕数据量小有对比就有说服力。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

hindsight:Chrome/Chromium浏览器取证与历史记录解析实战 2026/10/1 18:21:39

hindsight:Chrome/Chromium浏览器取证与历史记录解析实战

我第一次看到 hindsight 这个词的时候愣了一下。字面上它是“后见之明”,是站在现在回头看过去的通透感;但在安全工具圈,这个项目名字挂在一个挺能打的浏览器取证工具上。简单说,hindsight 是一个专门解析 Chrome / Chromium 系浏…

阅读更多 →
Hindsight Experience Replay:用事后经验回放破解强化学习稀疏奖励难题 2026/10/1 18:21:39

Hindsight Experience Replay:用事后经验回放破解强化学习稀疏奖励难题

挺有意思的是,"hindsight" 这个词在强化学习圈子里有着一个特别的分量。它原本是英文里"后见之明"的意思,也就是事后看问题都清清楚楚、当时却当局者迷——但在 2018 年之后,只要做决策智能、做机器人控制、做游戏 AI 的…

阅读更多 →
RML2016数据生成代码详解:从I/Q样本到调制识别训练集 2026/10/1 18:21:39

RML2016数据生成代码详解:从I/Q样本到调制识别训练集

简介:RML2016数据生成代码是面向无线通信与信号处理研究者的实用工具,基于Gnuradio与Python实现RML2016a数据集的复现与自定义生成,适合需要模拟Wi-Fi、LTE、蓝牙等调制信号及信道场景的算法验证工作。压缩包共12个文件,大小约25.…

阅读更多 →
AI资讯日报类内容策划与写作规范指南 2026/10/1 18:21:39

AI资讯日报类内容策划与写作规范指南

我无法根据当前输入生成符合要求的博文。原因如下:输入中项目标题为“2026-09-22 AI最新资讯日报”,但该日期尚未到来(当前为2024年),属于未来时间点,不具备可验证、可复现、可操作的现实基础;项…

阅读更多 →
面波处理与剖面连接:解决地震台阵频散曲线拼接失效问题 2026/10/1 18:21:39

面波处理与剖面连接:解决地震台阵频散曲线拼接失效问题

简介:本资源是面向地球物理勘探从业者与高校地学专业师生的面波数据处理与速度建模专用工具集,聚焦浅层地质结构反演中的核心环节——面波频散分析与多测线剖面连接。包含CCSWSWIN(面波处理)与CCSWSMAP(速度分层建模&a…

阅读更多 →
Hindsight Experience Replay:用后见经验回放破解稀疏奖励难题 2026/10/1 18:21:32

Hindsight Experience Replay:用后见经验回放破解稀疏奖励难题

我到现在还记得第一次跑Fetch Pick-and-Place任务时的场景:DDPG跑了三百万步,成功率始终保持静止的0%。当时的导师说了一句话:试试Hindsight吧。而正是这个叫Hindsight的反直觉思路,让我第一次理解了什么叫“从失败中学习”。Hind…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉