简介:一份围绕云南旅游景点数据分析与可视化的毕业论文文档,可供计算机或旅游管理相关专业的学生完成毕业设计、课程论文时参考,解决从景点数据处理到可视化系统设计的思路问题。文档从开发背景和国内外现状讲起,依次介绍了Python语言、Django框架、MySQL数据库及IDEA开发平台的选择理由,并说明了前后端分离架构、首页、用户管理、景点管理和旅游资讯发布等模块。还专门讲解了数据分析与可视化的方法流程,包括数据清洗、统计分析以及用图表地图展示结果,最后给出了总结与展望。资源包内共1个docx文件,大小约2MB,包含摘要、关键词、目录、正文与参考文献等完整论文结构。已有188人学习下载,对于需要完成类似课题的读者,可以直接借鉴系统模块划分、技术选型理由和可视化实现思路,减少选题与架构设计阶段的摸索。
1. 云南旅游景点数据分析:从论文标题到可落地项目,差的是数据管道
把一份标注着“基于Python的云南旅游景点数据分析与可视化”的docx论文标题抛给我,我第一反应不是打开文档,而是反问他:你手里有没有一份能画出云南地图、能按城市聚合、能解释“为什么丽江口碑高、昆明热度高”的干净数据?这个标题真正在讲的,是一套“Python 数据分析与可视化”的完整项目链路:数据获取、清洗、聚合、绘图、结论解释。它能解决的是旅游从业者、文旅运营人员和做毕业设计的学生最常碰到的问题——信息散落在各平台,想得到结论却不知道从哪下手。适合谁?适合已经会基础Python语法、想用真实业务数据做一次完整实战的人。先提醒一句:热门景区不等于高评分景区,这个反直觉结论,正是这个项目第一个能写进论文或报告里的洞察。
2. 数据准备与字段设计:先定数据字典,再谈爬虫和可视化
标题里带“景点数据分析”,第一步不是写爬虫,而是想清楚分析什么。我把常见做法拆成三条路:第一,直接使用公开旅游统计公报和景区名录,数据规范但更新慢;第二,从公开点评平台抓取结构化字段,数据鲜活但有合规和反爬风险;第三,自己造一份带随机种子、量级合理的示例数据,先把分析代码和图表跑通,再替换真实数据。我之前跑这个方向时,选的是第三条路打底,再补真实公报做交叉验证。
2.1 为什么选云南:数据好拿、结论可验证、可视化素材丰富
云南这个选题适合做数据项目,是因为它的地理层级和旅游数据天然对得上。省内16个市州,昆明、大理、丽江、西双版纳、香格里拉、腾冲各有不同的旅游标签,景区类型覆盖古城、雪山、雨林、温泉。做出来的地图和柱状图,观点一眼能看懂,不是那种“为了画图而画图”的产出。另外,云南旅游的热度数据在公开平台上有足够的评论量级,年接待游客量也有官方统计公报可做交叉验证。标题里写的是“云南旅游景点”,不是“全国景点”,这说明作者预设的地域边界很清晰。项目能落地的关键,也恰恰是把“数据管道”修到云南这一个域内。
2.2 数据字典先于代码:一张表定下字段与口径
我一般会先画一张字段表,而不是先写爬虫脚本。原因很简单:爬虫爬到一半发现缺字段,回头补的代价远大于一开始多写两行。下面这组字段是跑云南景点分析够用的最小集,所有后续计算都以它为准:
| 字段 | 类型 | 说明 | 来源方式 |
|---|---|---|---|
| city | str | 所属市州(昆明、大理、丽江等) | 基础字段 |
| scenic_name | str | 景点名称 | 基础字段 |
| latitude / longitude | float | 景点经纬度 | 地理编码或POI接口 |
| star_rating | float | 景区等级(3A/4A/5A,无等级填0) | 公开名录 |
| comment_score | float | 游客评分,百分制 | 点评页聚合 |
| comment_count | int | 评价数,作为热度代理变量 | 点评页聚合 |
| avg_price | float | 门票均价 | 订票页结构化字段 |
| peak_month | str | 主要旺季月份类别 | 业务规则标注 |
这份字典里有两个关键口径需要先定死:评分统一转成百分制,50分的及格线写法后面才有统一锚点;热度不用“游客量”而用“评论数”,是因为对普通项目来说评论数更好抓、更新更快。口径一旦定下来,后面所有清洗和聚合代码都长在同一个对齐锚点上。
2.3 用Python造一份可复现的示例数据:numpy加pandas脚本
真实数据没到位之前,先跑通数据处理链路是最值得的投资。下面这段代码生成500条覆盖10个市州的示例数据,带随机种子保证每次运行结果一致,省去“为什么换台机器图全变了”的烦躁。
import numpy as np import pandas as pd cities = ["昆明", "大理", "丽江", "西双版纳", "香格里拉", "腾冲", "红河", "玉溪", "曲靖", "怒江"] scenic_pool = ["古城", "雪山", "森林公园", "古镇", "湖泊", "峡谷", "梯田", "温泉", "茶园", "寺庙"] np.random.seed(42) # 固定随机种子,保证项目可复现 n = 500 # 样本规模 city = np.random.choice(cities, size=n, p=[0.15,0.15,0.12,0.12,0.10, 0.10,0.08,0.08,0.06,0.04]) scenic_name = city + "·" + np.random.choice(scenic_pool, size=n) latitude = np.random.normal(25.5, 2.0, size=n).round(4) longitude = np.random.normal(102.5, 3.0, size=n).round(4) star_rating = np.random.choice([0, 3, 4, 5], size=n, p=[0.2, 0.3, 0.3, 0.2]) comment_score = np.random.normal(86, 8, size=n).clip(0, 100).round(1) comment_count = np.random.lognormal(5.5, 1.2, size=n).astype(int) + 5 avg_price = np.random.lognormal(4.2, 0.6, size=n).round(0) df = pd.DataFrame({ "city": city, "scenic_name": scenic_name, "latitude": latitude, "longitude": longitude, "star_rating": star_rating, "comment_score": comment_score, "comment_count": comment_count, "avg_price": avg_price }) df.to_csv("yunnan_scenic_raw.csv", index=False, encoding="utf-8-sig")这段代码在生成数据时已经埋进了“现实世界”的噪声:评分为正态分布且截断在0到100分之间,符合点评平台大多数景区集中在75到95分的真实形态;评论数用对数正态分布生成,模拟少部分头部景区评论过万、大部分景区只有几百条的长尾结构。参数里最值得调的是np.random.seed(42)把随机过程固定,后续无论谁运行同一份脚本,得到的数据集都一致。p=[...]则让城市分布不均,昆明和大理占比更高,避免生成一份“十城均等”的假数据。
注意,这份CSV最终用utf-8-sig编码写入,是为了让Excel和Windows记事本直接打开不出现中文乱码。如果你想换成真实爬虫数据,只要把爬虫结果整理成同样的列名结构,后面清洗和可视化代码一行都不用改。
3. 数据清洗与聚合:用Python把脏数据变成“热度”和“口碑”两个指标
很多数据分析的新手在这一步翻车:爬下来5000条数据就直接groupby画图,图一出来就傻眼。做数据分析与数据挖掘实战项目,第一课永远是处理脏数据。所谓“脏”,不只是缺失和重复,还包括评分口径不统一、评论数极端值把图压扁、同一景点出现两条不同名称的记录。清洗不是论文里的理论章节,它是可视化之前必须在本地解决的现实工程问题。
3.1 清洗套路:去重、补缺、截断异常值
常见的数据清洗顺序是先去重、再补缺失、最后截断异常值。不要反过来——如果先截断再做去重,可能把“两个名字不同的同一条记录”留下,聚合时数据被重复计算。
import pandas as pd import numpy as np df = pd.read_csv("yunnan_scenic_raw.csv") # 第一步:按“城市+景点名”去重,保留评论数最多的那条记录 df = df.drop_duplicates(subset=["city", "scenic_name"], keep="first") # 第二步:缺失值补全 # 评论分缺失,用全国中位数补;价格缺失,用同城市中位数补 df["comment_score"] = df["comment_score"].fillna(df["comment_score"].median()) df["avg_price"] = df["avg_price"].fillna( df.groupby("city")["avg_price"].transform("median") ) # 第三步:截断异常值,避免极端值主导图表刻度和肉眼可读性 df["comment_score"] = df["comment_score"].clip(lower=0, upper=100) df["comment_count"] = df["comment_count"].clip( lower=df["comment_count"].quantile(0.01), upper=df["comment_count"].quantile(0.99) ) df.to_csv("yunnan_scenic_clean.csv", index=False, encoding="utf-8-sig")字段选择上,keep="first"保留首条记录在真实场景中不一定对,我一般会改成“保留评论数最多的一条”,因为评论数越多的记录越可能被完整维护。注释里已经写了这个逻辑,你实际执行时可以这样写:先按comment_count排序再drop_duplicates。clip的两个参数lower和upper用分位数而不是固定值,是为了让1%到99%区间之外的值被拉回边界,避免西双版纳某热门景点两万条评论把昆明几百条的散点全部挤压到原点附近。
3.2 聚合口径:按城市和按景区等级两个维度
清洗后的CSV要产生可分析结论,需要做两层聚合。第一层按城市,看整体口碑和热度分布;第二层按景区等级(5A/4A/3A/无等级),看高等级是否必然等于高口碑。这两层聚合是论文里最稳的两个表格来源。
# 城市维度聚合 city_stats = df.groupby("city").agg( scenic_num=("scenic_name", "count"), avg_score=("comment_score", "mean"), total_comments=("comment_count", "sum"), avg_price=("avg_price", "mean") ).reset_index().sort_values("avg_score", ascending=False) # 景区等级维度聚合 rating_stats = df.groupby("star_rating").agg( count=("scenic_name", "count"), mean_score=("comment_score", "mean"), total_comments=("comment_count", "sum") ).reset_index()这里有两个需要注意的口径陷阱。第一,avg_score用平均分而不是加权平均分,会造成小样本景区拖高城市均值,比如怒江如果只有两个景点且评分都高,它就会排在丽江前面,这不符合直觉。稳妥做法是加一层筛选:只保留景点数不少于5个的城市再排名。第二,star_rating等级为0表示无评级,它和3A景区混在一起聚合会拉平均值,可视化时建议单独标记为“未评级”,而不是算进“3A及以下”。
3.3 定义两个核心指标:热度与口碑,怎么算才不打架
论文不能只放“评论数”和“评分”两个原始字段,读者看不出结论。常见做法是把它们加工成两个0到100的标准化指标:口碑指数直接用comment_score的百分制,热度指数用comment_count做min-max归一化。为什么社交平台上有人批评“高热度榜”和“高口碑榜”完全对不上,就是因为这两个指标逻辑不同。计算热度时直接除最大值会让头部景区拿到100分,但长尾景区可能只有个位数,图表区分度很差,我一般会先做log1p再归一化。
# 热度指数:对数压缩 + 线性归一化到 0~100 df["heat_score"] = ( (np.log1p(df["comment_count"]) - np.log1p(df["comment_count"]).min()) / (np.log1p(df["comment_count"]).max() - np.log1p(df["comment_count"]).min()) * 100 ).round(1) # 口碑-热度四象限:用于后续散点图分组 df["quadrant"] = "普通型" df.loc[(df["heat_score"] >= 60) & (df["comment_score"] >= 85), "quadrant"] = "热门口碑型" df.loc[(df["heat_score"] >= 60) & (df["comment_score"] < 85), "quadrant"] = "热门争议型" df.loc[(df["heat_score"] < 60) & (df["comment_score"] >= 85), "quadrant"] = "小众口碑型"log1p的参数是加1再取对数,专门处理评论数为0的记录;60和85的分界线不是拍脑袋,是统计两组变量的中位数后取整得到的。想要更严格,可以用pd.qcut按四分位数自动切分,但论文里可解释性略微下降。四象限的意义在于:它让散点图不止是“大点小点”,而是直接生成“热门争议型”这种能写进分析结论的分组标签。
4. 可视化选型与实现:一张云南地图值回票价
可视化是这个项目里最出效果、也是最容易把论文做成“大杂烩”的部分。很多Python教程只教语法,不教怎么选图表,结果就是柱状图、折线图、饼图堆了一页,读者看完记不住任何结论。我建议只选三张图:云南省地图展示地理分布、热度-价格散点图展示市场结构、TOP15条形图展示头部景区,三张图对应三个核心观点。
4.1 工具选型:matplotlib和pyecharts,按使用场景选
常见做法是:论文终版插图用matplotlib和seaborn,黑白打印不失真,样式稳定;答辩现场或大屏汇报则用pyecharts,它生成的交互式HTML适合投屏滚动查看。另外一种组合是pyecharts画地图、matplotlib画散点和柱状图,两个库各干各最擅长的事。pyecharts的图表在网页端有悬浮提示,地图缩放流畅,标签可控;matplotlib胜在自由度和论文排版兼容性。我在这个项目里的选择是,地图给pyecharts,其余全部matplotlib,理由只有一个:散点图叠加四象限颜色用matplotlib五六行搞定,用pyecharts反而要翻文档。
4.2 用pyecharts画云南地图:市州维度的口碑分布
云南地图是这个项目里最出效果的一张图。pyecharts加载“云南”地图时,底层需要GeoJSON数据,不同版本处理方式差别很大。下面这段代码适用于pyecharts 2.x的标准用法。
from pyecharts import options as opts from pyecharts.charts import Map map_data = [ [row["city"], round(row["avg_score"], 2)] for _, row in city_stats.iterrows() ] m = ( Map() .add( "口碑均分", map_data, maptype="云南", is_map_symbol_show=False, # 关闭散点标记,避免遮挡颜色映射 is_roam=False, # 关闭缩放拖拽,论文截图更干净 ) .set_global_opts( title_opts=opts.TitleOpts(title="云南各市州景点口碑均分"), visualmap_opts=opts.VisualMapOpts( min_=80, max_=90, range_color=["#fde725", "#5dc863", "#21918c"], is_piecewise=False, # 连续色带,适合口碑这类连续指标 ), ) ) m.render("yunnan_map.html")这段代码里有两个参数值得单独说明。is_map_symbol_show=False把地图上的散点图标关掉,很多教程没加这一行,结果是云南地图上方铺了一层红点,颜色映射基本看不清。min_=80和max_=90不是什么玄学,它是从清洗后的口碑分位数里取出来的:查询city_stats["avg_score"].quantile([0.1, 0.9]),把区间稍微放宽,色带才能拉开层次;如果你用全量数据的min到max,比如60到95的跨度,全省基本一个颜色。运行完代码会在本地生成一个yunnan_map.html,浏览器打开后悬停可看城市名称和数值。
如果地图渲染出来是空白或者报“Map not found”,问题基本不在代码,而在pyecharts的地图数据文件。2.x版本对省级GeoJSON的注册方式比较麻烦,我习惯把离线注册文件放到项目根目录的geo文件夹中,通过Map.add前显式注册Geojson。这一点在第五章避坑里会细说。
4.3 热度-价格散点图:哪些景区“又贵又火”
散点图承载的是文章的核心洞见:横轴是热度指数,纵轴是门票均价,点的大小映射评分,点的颜色映射四象限。
import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] matplotlib.rcParams["axes.unicode_minus"] = False fig, ax = plt.subplots(figsize=(10, 6)) scatter = ax.scatter( df["heat_score"], df["avg_price"], s=df["comment_count"].apply(lambda x: max(10, x / 50)), c=df["quadrant"].map({"热门口碑型": "#e74c3c", "热门争议型": "#f39c12", "小众口碑型": "#2ecc71", "普通型": "#bdc3c7"}), alpha=0.6, ) ax.set_xlabel("热度指数(对数归一化)") ax.set_ylabel("门票均价(元)") ax.set_title("云南景区热度-价格分布与口碑分组") plt.tight_layout() plt.savefig("heat_price_scatter.png", dpi=300)散点图的三个参数是在真实项目里调试最久的。s=comment_count/50让评论量最大的景区点直径约为普通景区的3倍,这里除以50是调出来的经验值,如果点在图中明显过大可以改100;alpha=0.6让重叠点能透出层次,大量中低热度的点叠在一起时可读性好很多。四象限的颜色映射直接来自3.3节生成的quadrant列,不需要额外合并数据集。还有一个隐藏坑:如果你发现图里中文全是方框,那是matplotlib在Linux下找不到中文字体,先检查fc-list :lang=zh,没有就安装字体,这一条在第五章单独展开。
4.4 单景点TOP15条形图:论文里最稳妥的一类图
地图和散点图都有一定解读门槛,条形图则是论文里最安全的一类输出。热度TOP15条形图能让读者在3秒内理解“云南最火的景点是谁”。但要注意条形图和柱状图的方向——景点名称是长文本,横置条形(名字在Y轴)比竖置柱状图可读性强很多。
top15 = df.nlargest(15, "comment_count") fig, ax = plt.subplots(figsize=(8, 8)) colors = plt.cm.viridis(ax.scatter(top15["comment_count"], range(len(top15)), c=top15["heat_score"]).norm(top15["heat_score"])) ax.barh(top15["scenic_name"][::-1], top15["comment_count"][::-1], color=plt.cm.viridis(0.8)) ax.set_xlabel("评论数(条)") ax.set_title("云南热门景点 TOP15(按评论数)") plt.tight_layout() plt.savefig("top15_bar.png", dpi=300)这里有一处细节:[::-1]把数据倒序排列,因为barh的条形图是Y轴自下而上绘制的,不做倒序处理时最大值的条形会显示在最底部,视觉上很不舒服。颜色统一采用viridis色系的末段色,而不是用彩虹色逐条标颜色,这是为了打印时省墨、也维持统一的视觉风格。想要更专业一点,可以在每条柱子末端标注评论数,用ax.text循环加标签即可。
5. 云南旅游景点可视化避坑:5个高频翻车点与排查方案
这一章是血泪经验集。我能理解触到这类项目的痛点的人很多,因为这些坑会出现在任何“Python + 数据可视化”相关的项目里——地图加载、中文乱码、坐标偏移、长尾数据压扁图表、清洗结果和图表脱节。每一条我都按“现象→原因→解决”的格式写,方便你快速对号入座。
5.1 pyecharts地图白屏:不是代码有问题,是地图GeoJSON没注册
现象:本地打开yunnan_map.html,页面是空白,浏览器控制台报错类似于Map is not defined。
原因:pyecharts 2.x版本默认不自带省级GeoJSON数据,需要单独注册。很多Python教程里的地图代码能跑,是因为运行环境里已经装过额外的地图数据包;换一台新电脑就翻车。
解决:把云南省GeoJSON文件下载后放进项目geo目录,注册方式参考下面这段伪代码逻辑——先注册再绘图:
from pyecharts.datasets import register_url # 实际文件来自本地geo目录,不是网络请求 with open("geo/yunnan.json", "r", encoding="utf-8") as f: register_url("云南", "http://127.0.0.1:8000/yunnan.json") # 本地起服务或用DataURI方式这个方法有个别扭的点:register_url走的是URL,本地文件需要起一个静态服务。我做项目时更常直接用Map.add配合maptype="云南",若仍报错,就用最土但最有效的方法——把地图数据改成“全国”图,然后用VisualMapOpts的range_color和region参数只展示云南范围。这个方案不优雅但稳定,适合答辩前最后一晚救场。提前一天检查地图能否渲染,是唯一可靠的后悔药。
5.2 中文乱码和方框:编码与字体双重问题
现象:CSV打开中文乱码,matplotlib输出的图片里中文变成一个个方块。
原因:两个不同坑。CSV乱码是因为没有用utf-8-sig编码写入,Windows默认用GBK打开,utf-8的BOM缺失时读出来就是乱码。图片中文方框是matplotlib默认字体库在系统里找不到SimHei或微软雅黑。
解决:写入CSV时统一encoding="utf-8-sig";matplotlib侧写入字体配置:
import matplotlib import matplotlib.pyplot as plt matplotlib.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "Noto Sans CJK SC"] matplotlib.rcParams["axes.unicode_minus"] = False三组字体按顺序回退,Windows走SimHei,Linux走Noto Sans CJK SC。如果你在云服务器上画图,没装任何中文字体,直接apt install fonts-noto-cjk装完重跑即可。
5.3 坐标偏移:国内POI数据和底图坐标系不匹配
现象:散点图上的景点位置整体偏到山沟里,或者地图上标注的丽江古城落在城外两公里。
原因:国内大多数POI接口返回的是GCJ-02坐标(火星坐标系),而pyecharts地图底图用的经纬度基准是WGS84。两个坐标系之间存在几十到几百米的偏差,放到市级地图上不明显,放到景区级别的小比例尺图上就非常突出。
解决:在清洗阶段就把坐标统一转换。常见做法是维护一个gcj02_to_wgs84函数,网上流传的版本就是把火星坐标做一次偏心校正。我不建议用复杂的库,十几行函数足够。
import math def gcj02_to_wgs84(lng, lat): a = 6378245.0 ee = 0.00669342162296594323 dlat = _transform_lat(lng - 105.0, lat - 35.0) dlng = _transform_lng(lng - 105.0, lat - 35.0) radlat = lat / 180.0 * math.pi magic = math.sin(radlat) magic = 1 - ee * magic * magic sqrtmagic = math.sqrt(magic) dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * math.pi) dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * math.pi) return lng + dlng, lat - dlat这个函数不需要理解内部球体模型也能用。它接受GCJ-02经纬度,返回WGS84经纬度。使用方法是:在清洗脚本里对latitude和longitude列逐行转换,转换后的列加后缀_wgs84,再供图表使用。需要注意的是,这个公式依赖固定参考点,全国各地误差不完全一致,但对云南这一地域范围来说足够可靠。
5.4 长尾数据把图表压扁:先看分布再决定要不要截断
现象:热度散点图中,90%的点堆在左下角一个小区域,个别点飞在右上角,整张图的信息量约等于零。
原因:评论数量级差异过大。头部景区上万条,长尾景区几十条,线性坐标直接画,长尾必然被压扁。
解决:不仅做log1p变换,还要观察变换后的分布图再决定坐标轴范围。我通常先跑一行:
df["log_count"] = np.log1p(df["comment_count"]) df["log_count"].hist(bins=40)如果直方图显示大部分点在5到8之间、尾部在11以上,按0.01到0.99分位数截断后再绘图。如果截断后仍然偏斜,要考虑用pandas.cut把热度分箱成“高/中/低”三个等级,用颜色表达而不是用位置表达。
5.5 清洗逻辑更新后图表没同步:数据和图形耦合太深
现象:改了清洗脚本后发现图还是旧数据,或者直接把两个脚本放在同一个文件里跑,中间任何一步报错图就全没输出。
原因:数据分析项目中常见的架构问题——清洗脚本和绘图脚本混在一起,函数内部直接引用了清洗前的DataFrame变量。
解决:强制把清洗结果落盘,绘图脚本单独读CSV。清洗输出yunnan_scenic_clean.csv,绘图脚本开头只保留一行pd.read_csv。这样既隔离了报错范围,也让论文每一张图都能追溯数据版本。我自己的习惯是加点版本号:清洗输出存成yunnan_scenic_clean_v2.csv,图表的图注里同步标注数据日期,答辩时这个细节能给评审留下深刻印象。
6. 进阶技巧:从单张静态图升级成可筛选的“数据看板”
到这里,你已经有了清洗后的CSV、三张核心图表和一个可以写进论文的结论。下一步我想把它升级成一个真正能被翻来覆去查看的轻量“数据看板”:用Flask暴露一个简单的数据API,前端用pyecharts读取接口,按城市切换图表。数据看板不是大屏,但它的交互价值在于——论文答辩时,评审问一句“那大理的数据单独看不呢”,你在浏览器下拉选择“大理”,图表实时切换,比返回到脚本里改参数重跑一遍体面得多。
首选方案是Flask加少量JavaScript,听起来要写前端,其实后端模板里嵌一个pyecharts的Page就能解决大半需求。最小实现如下:先把聚合结果按城市存成JSON接口,再在HTML里加载地图脚本。
from flask import Flask, jsonify import pandas as pd app = Flask(__name__) df = pd.read_csv("yunnan_scenic_clean.csv") @app.route("/api/city/<city>") def city_snapshot(city): rows = df[df["city"] == city] return jsonify({ "scenic_num": len(rows), "avg_score": round(rows["comment_score"].mean(), 2), "total_comments": int(rows["comment_count"].sum()), "top_scenic": rows.nlargest(3, "comment_count")["scenic_name"].tolist() }) if __name__ == "__main__": app.run(debug=False, port=5000)这份接口代码返回的是一个城市的场景快照:景点数量、平均口碑、总评论数和TOP3景点名。前端拿到数据后,可以直接拼装成卡片的HTML文本,也可以把数值喂给ECharts重新渲染。这里的要点是debug=False——调试时你可以开True,但部署展示时一定关掉,否则刷新页面容易暴露出栈信息。
做完看板之后,不要忘了验证数据可信度。我的习惯是把聚合结果和云南省的公开统计公报数字对一遍:公报说某市年接待游客量是5000万,再看评论总数是否在同一量级,差了一个数量级以上一定是数据口径出了问题。相关性验证也可以做:把城市维度的热度指数和百度指数月均值做一次Spearman相关,相关度高于0.6基本能说明这一套数据管道是靠谱的。
我现在的习惯是,任何数据项目开工前先强制自己写一份数据字典,清洗和绘图必须分离成两个脚本。抓到数据先画分布图再谈结论,这句话我吃了无数次亏才真正听进去。沿着这套方案把云南旅游景点分析做下来,你会得到一份能复现的本地项目,也顺带把“数据管道”这个词变成了自己的实操能力。希望帮到你。
本文还有配套的精品资源,点击获取