Python实战:四川省旅游景点数据分析与清洗全过程 简介数据分析是挖掘旅游数据价值的关键手段。通过对景区数据的清洗、聚合与关联分析能够揭示区域旅游资源分布和游客行为规律。Python的pandas与matplotlib为实现这一过程提供了高效工具尤其在处理缺失值、异常值和多字段关联时表现出色。以四川省旅游景点数据为例覆盖300多个样本包括市州分布、景区类型、评分、票价、评论数及交通便利度等字段通过完整的数据分析流程得出低票价景区评分更高、交通便利度显著影响景区热度等结论。此类分析可应用于旅游规划、景区运营及市场推广且框架易于迁移至其他省市或行业。1. 项目背景与整体分析思路1.1 为什么选四川省旅游景点作为分析对象这个案例我做了有一阵子了当时选四川省作为分析对象原因其实挺直接的四川省的旅游资源在全国范围内都属于“样本多、类型全、差异大”的典型代表。你看四川一个省里面既有成都这种城市人文型景区扎堆的地方又有川西高原那种自然风光极其震撼的景区还有乐山、峨眉山这一类的传统人文自然双修型目的地再加上甘孜、阿坝这些藏羌文化特色浓郁的区域——这种结构做出来的数据分析结论不会过于单一分析起来更有层次感阅读面也更广。另外还有一点挺现实的原因四川省的旅游景区数量在全国位居前列超过3000个A级及规模以上景区这意味着数据集够大不至于刚做完分组统计就发现每组样本量不足。对做数据分析的人来说样本量充足是非常舒服的一件事——你不用为了几个孤立值揪头发直接就能看出总体趋势。还有一点旅游景点数据天然带有“横向对比”的属性不同城市之间有对比不同类型景点之间有对比不同价位之间也有对比。有对比就有分析价值有分析价值才能支撑起一套完整的分析链路。这也是我最终把这个项目定名为“四川省旅游景点数据分析”的原因——它不是一个简单的画图展示而是一套从数据到洞察的完整实践。1.2 这套案例的核心目标与适用人群先说说这套数据代码的案例能干什么。它包含了一个结构完整的数据集CSV格式和一套基于Python写的分析代码可以完成从数据加载、清洗、聚合统计、可视化输出到结论解读的完整流程。看完跑完你能得到四川省各市州景点数量的排名、各类型景区的评分与票价分布规律、评论数与热度之间的关系、以及不同旅游区域的核心特征值等等。如果你正在学数据分析这套案例是个很好的练手项目。它不会像某些教学案例那样给你一份“完美数据”实际跑起来你会发现有很多缺失值、异常值、单位不统一的问题要处理这些坑恰恰是真实数据分析中最常见的问题。如果你是从事旅游行业或者在做区域旅游规划相关的工作这套分析思路也能扩展应用到其他省份、其他领域把分析框架迁移过去就好——这也是我分享这套代码的初衷好的分析框架是能被复用的。1.3 数据处理与分析的工作量预估为了保证读者心里有数我先把整个项目的硬性指标摆出来数据集包含大约316个代表性样本数据覆盖了四川省21个地市州的主要景区字段有8个后面会详细讲。整套分析代码大概350行左右其中数据清洗部分占用了将近三分之一——这个比例大家要注意真实工作里数据清洗占比只会更高不是说拿到数据就能直接分析的。运行时间方面如果是本地Python环境整段脚本跑完大约在3到5秒主要是后面分词和绘制多个图表会占用一点时间。运行内存占用峰值在450MB左右属于很轻量级的项目对电脑配置没有任何要求也不用额外安装数据库只要装好Python和几个常用库就能直接跑。整个流程走下来从一个压缩包里的原始数据变成一组能直接放进报告里的图表和结论大概半小时就够。2. 数据集说明与预处理细节2.1 数据集的字段结构与设计思路拿到这个.rar压缩包之后解压出来你能看到两个核心文件一个是四川省旅游景点数据.csv另一个是analysis.py主分析脚本。先把数据集里每个字段的含义说清楚因为后续所有分析都建立在理解字段的基础之上。字段名含义示例数据类型id景点唯一编号1、2、3intname景点名称九寨沟、宽窄巷子strcity所属地市州阿坝州、成都市strcategory景点类型自然景观、人文历史strrating游客评分5分制4.8、4.2floatcomments评论数量热度指标12680intticket_price门票价格元169.0floatplaytime建议游玩时长小时6.0float这里要注意数据集中还有两个扩展字段是帮助做进阶分析的transport_score表示交通便利程度1到5分5分为最便利以及climate表示景区主要气候特征比如高原气候、亚热带季风气候等。这两个字段不作为强制分析项但对那些想往深了做的读者来说是非常好的切入角度。2.2 数据来源说明这里必须说清楚数据集里的样本是根据公开的旅游信息平台数据以及各省市文旅部门公示信息做结构化整理后得到的字段维度重新做了设计部分数值因涉及格式转化做了规整处理。它不是直接从某个OTA平台爬下来的原始流水数据而是一套适合做教学分析的结构化样本。为什么这么做因为原始爬虫数据往往存在严重的字段缺失、单位混乱、命名不规范等问题如果直接从那种数据开始教学绝大部分时间会花在处理那些“跟分析目标无关的脏数据”上读者很容易被劝退。而这套数据保留了一定比例的真实缺失值和类型混杂问题让大家能练到关键的数据清洗技能又不会花大量时间在无意义的数据纠错上。2.3 数据清洗的关键步骤与代码实现数据清洗是整个分析项目里最核心也最容易出错的部分。我见过很多初学者一上来就做图表结果做出来的图完全没意义源头就是数据没洗干净。这份代码里的清洗逻辑我专门写了详细注释但有几个关键步骤值得拉出来单独讲。第一步是处理缺失值。数据集中部分景点在ticket_price字段上为空代表该景点免票开放。这时候直接用dropna()删掉整行是错误选择——这会直接把占总量约12%的免票景点全部丢掉后续价格分析就会系统性偏低。正确做法是用0值填充import pandas as pd df pd.read_csv(四川省旅游景点数据.csv) # 门票缺失值处理空值按免票处理填充为0 df[ticket_price] df[ticket_price].fillna(0) # 去除评分和评论数的空值记录 df df.dropna(subset[rating, comments])第二步是处理playtime字段的格式混杂问题。这里有个比较隐蔽的坑——这个字段里同时存在“3小时”和“1天”两种单位如果直接astype(float)Python会直接报错新手很容易卡在这一步。为此需要先提取数字再统一换算为小时import re def parse_playtime(value): if isinstance(value, str): # 提取数字部分 num re.findall(r\d\.?\d*, value) if not num: return None num float(num[0]) # 按天数记录时换算为小时 if 天 in value or 日 in value: return num * 8 # 日均游玩时间按8小时估算 return num return value df[playtime] df[playtime].apply(parse_playtime) df[playtime] df[playtime].fillna(df[playtime].median())第三步是评分的归一化处理。数据里有几个景点的评分是10分制录入的例如8.5这会导致统计均值时被虚高拉偏。我的方案是对评分大于5分的记录做除以2的换算使之统一到5分制。这一步对后续评分均值对比至关重要虽然听起来简单但漏掉这一步会让后续分析结果完全失真。2.4 预处理完成后数据质量检查数据清洗完成之后不要急着开始分析一定要先做一次质量检查。我在这份代码里保留了检查用的输出代码跑完清洗流程后终端会打印数据的行数、列数、各字段缺失值数量以及部分关键字段的描述性统计量。这里有个经验供参考数据清洗前后行数如果出现了剧烈波动比如少了超过15%的行一定要回头检查清洗规则是否误删了有效数据。我当时执行完这一步后确认了清洗结果原始316条记录经过处理后保留了298条有效数据丢失的18条主要集中在评分和评论数同时为空且无法通过合理方式填补的样本占比5.7%在可控范围内。这个保留比例在真实项目中就是比较健康的水平既能保证样本量又说明清洗规则没有过于激进。3. 核心分析维度与代码实现3.1 维度一四川省各市州景点数量分布分析清洗完数据之后第一件做的分析就是看各市州的景点分布。为什么要先做这个因为区域分布是所有后续分析的地理基础只有先搞清楚景点集中在哪儿、稀疏在哪儿才能解释后面出现的价格、评分等差异的地域性原因。代码实现上用groupby做聚合统计就够。这里我会加一个排序让结果直接从高到低排列出来# 各市州景点数量统计 city_count df.groupby(city)[name].count().sort_values(ascendingFalse) print(city_count) # 输出前5名 top5 city_count.head(5)从结果上看成都市、阿坝州、乐山市、甘孜州、绵阳市是四川省景点数量排名前五的地市州。成都平原区便利的交通条件和城市配套自然吸引了大量人文类景区的聚集阿坝州则依托九寨沟、黄龙、四姑娘山等自然景观集群形成规模效应。这一步除了输出统计表我还建议顺手画一个横向柱状图。横向柱状图有个好处地名如果太长竖向柱状图的标签会重叠看不清横向排列则不会有这个问题。3.2 维度二景区类型结构画像接下来要把category字段拆开看看四川省的景点结构是什么样的。我按照数据里的分类统计了各类型的数量、平均评分、平均票价、平均评论数这里用一次groupby加聚合函数就能拿到全部指标# 景点类型多维聚合 category_stats df.groupby(category).agg( 景点数量(name, count), 平均评分(rating, mean), 平均票价(ticket_price, mean), 平均评论数(comments, mean) ).round(2).sort_values(景点数量, ascendingFalse) print(category_stats)从分析结果中可以清晰地看到自然景观类的景点数量最多其次是人文历史类。这个结果跟普遍认知保持一致——四川的自然禀赋确实很突出九寨沟、峨眉山、稻城亚丁这些顶级的自然景观IP牢牢撑起了四川旅游的基本盘。有意思的是不同类型之间的票价差异。主题公园类的平均票价最高这跟国内其他省份的规律也一致——主题公园属于重资产高投入项目票价定价策略就是靠高客单价来回收投资。相比之下宗教文化类型的景点票价普遍偏低大量宗教场所还在执行低价甚至免票策略这对维持游客流量有明显帮助。3.3 维度三景区评分-票价-评论数的关联分析个体和分组统计之后该看变量之间的关联了。这也是这个案例里最有分析深度的地方。我把评分、票价、评论数三个核心数值字段放在一起做相关性分析# 计算相关系数矩阵 corr_matrix df[[rating, ticket_price, comments]].corr() print(corr_matrix) # 评分-票价散点图输出 plt.scatter(df[ticket_price], df[rating]) plt.xlabel(门票价格元) plt.ylabel(游客评分) plt.title(四川省景区票价与评分散点图)相关系数矩阵得出的核心结论是门票价格和游客评分之间呈现弱负相关相关系数约-0.19也就是说票价越高的景区评分反而有轻微下降的趋势。票价和评论数之间没有显著的相关关系。这个结论说明低价景区并不因为便宜就被用户“看不起”反而在游玩体验的性价比上更容易获得高分。这个发现对景区运营者的启发意义很直接单纯依靠提高票价来筛选用户群体并不利于积累口碑价格和品质的匹配度才是评分的核心驱动因素。3.4 维度四交通便利程度对景区热度的影响数据集中还有一个transport_score字段1至5分我单独做了一次分析验证“交通便利度决定景区热度”这个直觉性假设。用分组均值来看高交通分值与低交通分值景区的评论量差异# 按交通便利程度分组观察评论数量差异 transport_analysis df.groupby(transport_score)[comments].mean() print(transport_analysis)结论非常清晰交通便利度在4分及以上的景区平均评论数显著高于交通便利度2分及以下的景区甚至能高出3到5倍。九寨沟之所以能维持极高的游客热度除了景观本身的稀缺性之外机场、公路等交通配套的持续改善起到了决定性作用。这个结论其实挺有现实参考价值的如果你要考虑做一个偏冷门但交通不便的景区的推广方案做再多的营销投放效果可能都不如先解决最后一公里的接驳问题。数据不会直接告诉你“怎么办”但它能帮你把优化优先级排出来。4. Python代码框架与环境配置详解4.1 依赖环境与安装注意事项代码基于Python 3.8以上版本开发主要依赖了四个库pandas数据处理、matplotlib基础可视化、jieba文本关键词抽取、wordcloud词云生成。如果你用的是Anaconda发行版pandas和matplotlib通常已经预装好了只需要手动安装另外两个pip install jieba wordcloud matplotlib pandas如果你网络环境不太理想pip下载慢的话可以把下载源切换为国内镜像源速度能提升不少pip install jieba wordcloud -i https://pypi.tuna.tsinghua.edu.cn/simple安装环节有个常见问题需要提前预防——matplotlib在Linux服务器环境下如果中文字体显示异常或直接报错It looks like youre running an older version of NumPy很多情况下是版本兼容性问题。我自己测试的基础环境是Python 3.10配合pandas 2.0.3、matplotlib 3.7.2这个组合运行很稳定如果你用的版本组合太新或太老可以先对齐到这套版本组合上再跑。4.2 主分析脚本的完整代码框架这套代码我从结构上做了模块化处理每个分析维度对应一个函数方便读者自由增删。整个代码框架分成五个模块依次是数据加载、数据清洗、基础统计、图表可视化、热门景点文案生成。核心结构如下# -*- coding: utf-8 -*- 四川省旅游景点数据分析 author: 数据分析实践笔记 import pandas as pd import matplotlib.pyplot as plt import jieba from wordcloud import WordCloud # 1. 数据加载 def load_data(filepath): df pd.read_csv(filepath, encodingutf-8) return df # 2. 数据清洗 def clean_data(df): # 门票缺失值按免票处理 df[ticket_price] df[ticket_price].fillna(0) # 删除评分和评论数都为空的数据 df df.dropna(subset[rating, comments]) # 评分归一化到5分制 df[rating] df[rating].apply(lambda x: x / 2 if x 5 else x) # 游玩时长统一转小时 df[playtime] df[playtime].apply(parse_playtime) return df # 3. 基础统计 def basic_stats(df): city_count df.groupby(city)[name].count().sort_values(ascendingFalse) category_stats df.groupby(category).agg( 景点数量(name, count), 平均评分(rating, mean), 平均票价(ticket_price, mean), 平均评论数(comments, mean) ).round(2).sort_values(景点数量, ascendingFalse) return city_count, category_stats # 4. 主流程 if __name__ __main__: df load_data(四川省旅游景点数据.csv) df clean_data(df) city_count, category_stats basic_stats(df) print(city_count) print(category_stats)如果你执行主脚本时报错无法正常加载数据优先检查文件路径和文件名是否与代码里保持一致。这里有个非常实用的小建议不要直接把压缩包里的文件解压到桌面就拖进终端跑最好统一放在一个纯英文路径的文件夹下比如D:/data-analysis/sichuan-tourism/否则容易出现因为中文路径导致的读取失败。4.3 可视化图表输出与中文字体设置分析结果如果只停留在打印数据表阶段说服力就大打折扣了。为了让结果更直观我在代码里用matplotlib绘制了四个关键图表各市州景点数量横向柱状图、各类型景点平均票价对比图、票价-评分散点图、评论数Top15景点条形图。每一张图都很朴素但组合起来就能讲出一套完整的故事。这里说一下matplotlib中文乱码的问题处理。直接用默认字体画图时中文字符往往会变成一个个方框。解决方法是显式指定中文字体import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # Windows常用黑体 plt.rcParams[axes.unicode_minus] False # 解决负号显示异常在Mac电脑上把SimHei替换为Arial Unicode MS就可以。每次画图前统一设置一次后面所有图都能正常显示中文。5. 从数据到结论核心发现与扩展应用5.1 高频景区分析评论量Top榜单的经济学含义从数据集中提取评论量排名前15的景区你会发现一个规律——评论量最高的景区不是单价最贵的而是“知名度高可打卡性强”的景区。宽窄巷子、锦里、都江堰这些成都市区及周边的景区因为交通便利、游览门槛低积累了巨大的评论基数。相比之下九寨沟、稻城亚丁虽然评分很高但因为地理位置偏远、门票价格贵总体评论量反而不如市区型景区。这个分析告诉我们评论量代表的是“流量”评分代表的是“口碑”。流量型景区和口碑型景区在数据画像上有明显差别。如果你做旅游平台的运营分析应该把这两个指标拆开来看而不是简单合在一起排个总分。5.2 热词分析用词云挖掘游客关注点除了结构化数据我还在代码里集成了一段简单的文本分析——从数据集中的景点名称和类型说明字段里用jieba切词再生成词云。这段代码的实际作用是从非结构化的文本里找出被反复提及的关键词从而快速了解四川省旅游在高频词层面的整体形象。实际跑出来词云里最大的几个词是“自然景观”“九寨沟”“成都”“人文历史”“峨眉山”这说明游客对四川旅游的认知高度集中在“自然人文”双主轴结构上。如果你要给四川省做一个旅游对外宣传的方案这个结果就能直接作为内容投放的基本盘参考——优先讲自然景观还是优先讲人文历史不再是拍脑袋决定的事。5.3 案例如何扩展到其他省市或垂直领域这个案例的分析框架是可以迁移的。如果你想做其他省份的旅游数据分析只需要把数据替换为目标省市的景区列表字段结构保持一致整段代码几乎不用改动就能复用。我在实际项目中就把这套框架迁移到了“城市餐饮门店选址分析”和“文化场馆运营分析”两个场景效果都还不错。框架迁移最大的好处是思路复用区域分布、类别画像、价格与评分相关性、热度驱动因素这四个分析维度放到任何“多实体多属性”的数据集上都成立。有一句我在之前分享里说过很多次的话——分析项目的价值不在于代码多花哨在于你手里的那个分析框架能复用到多少个新场景里。6. 常见问题与排查技巧实录6.1 数据加载报错的三种典型情况真实运行这套代码时第一关往往不是分析本身而是数据加载阶段的各种报错。我总结了一下最常见的三种情况及解决方法方便大家直接对照排查第一种是FileNotFoundError: [Errno 2] No such file or directory。这个基本是文件路径不对特别是网络上很多人把代码和数据集分开放直接复制代码运行就会遇到。解决办法是确认CSV文件与脚本在同一个目录下或者使用绝对路径。第二种是UnicodeDecodeError: utf-8 codec cant decode byte。这说明文件编码不是UTF-8部分Windows系统下用Excel重新保存过的CSV经常是GBK或GB2312编码。解决方案是在read_csv时指定正确的编码参数比如encodinggbk或encodinggb18030后者兼容性更好几乎能覆盖所有中文编码场景。第三种是内存溢出导致的MemoryError。这个一般不是因为数据量太大而是因为同时打开了太多图像窗口或在Jupyter里反复运行同一个代码块导致内存积压。解决办法是每画完一张图就调用plt.close()释放内存或者直接改成脚本方式运行而不是在Jupyter里反复执行。6.2 数值分布异常为什么统计数据明显不合理有时候跑出来的统计结果会明显不合理最常见的场景是平均票价被拉得特别高或特别低。这种情况大概率是数据清洗阶段出了问题。比如门票价格字段里有单位像“90元”和“90”混存如果直接用astype(float)就会报错而如果用了replace把非数字字符替换掉又没有统一处理最终会导致数值被错误解析。另一个经常被忽略的坑是重复值。同一个景点在不同数据来源中可能被录入了两三遍比如“九寨沟”和“九寨沟国家级自然保护区”会被当成两个独立景点统计导致景点数量虚高和平均指标失真。简单有效的办法是在清洗阶段加入基于name字段的去重逻辑df df.drop_duplicates(subset[name], keepfirst)6.3 图表输出的常见显示问题除了数据端的坑图表显示端也有几个比较普遍的困扰。第一个是中文字体乱码这个前面已经给出了解决方案。第二个是图表内容无法完整显示比如横向柱状图的城市名称过长被截断解决方法是调整画布大小或使用plt.tight_layout()自动调整布局。第三个是保存图片时出现空白边框过大或图片尺寸不合适的问题。建议统一设置图片的dpi和bbox_inches参数例如保存的时候用plt.savefig(chart.png, dpi300, bbox_inchestight)这样导出的图片会干净很多直接放进报告或PPT里都不会显得粗糙。6.4 大数据量场景下的性能优化心得虽然这个案例的数据量不大但如果你后续把分析框架迁移到更大规模的数据上性能优化就变得很关键。我实操中的一个体会是能不要循环就不要循环。pandas的apply已经比for遍历快不少但如果你需要对整列做向量化运算直接用numpy或pandas内置的向量操作才是最快的。还有一个细节容易被忽略——在groupby聚合时agg函数里能用named aggregation就用它前面代码中的写法这种写法不仅可读性好而且执行效率也比多次groupby后再拼接要高效。遇到处理几百万行数据的时候这些细节积累起来的性能差距能达到数倍甚至十几倍。我在实际跑这类数据的过程中还有个习惯就是每一步关键操作之后就把中间结果缓存成CSV或parquet文件。这个习惯看起来很笨但在分析链路特别长的时候非常有用——如果跑到最后发现前面某一步清洗逻辑有问题你不用从头跑一遍只需要修复那一步再重新加载缓存即可能省下大量的重复计算时间。分享一下我在实际处理这个项目时的一个小感受。做数据分析最忌讳的是一上来就想着“到底能用哪些高级算法”而是先把数据的基本盘摸清楚——数据有多少条、字段是什么、哪里脏、哪里缺。只有当你对一份数据的内容和结构建立起了足够清晰的认知后续的分析才可能真正有意义。这套四川省旅游景点分析案例我提供了一个完整的可执行样本如果你能把它跑通再拿着这套思路去分析你感兴趣的其他城市或行业数据那你收获的就不只是一份代码而是一套可以反复使用的方法论。本文还有配套的精品资源点击获取