资讯详情 Python机票数据分析与可视化大作业:从数据清洗到Pyecharts交互大屏
📅 2026/10/3 4:07:37
简介这份资源是面向计算机相关专业学生与项目实战学习者的Python数据分析可视化大作业完整方案聚焦机票价格数据分析与预测并借助PyQt5完成可视化界面展示适合作为课程设计、期末大作业或技能练习的参考项目。压缩包共37个文件约1.33MB包含11个Python源码文件、9个CSV数据集、6个PNG图表、6个XML配置、2个UI界面文件及说明文档等覆盖数据预处理、回归分析、回归可视化与数据可视化等模块目录结构清晰便于按功能模块查阅与二次开发。目前已有402人学习下载。项目经导师指导并认可评审分99分代码完整可运行读者可据此掌握从原始数据清洗、价格影响因素分析到预测建模与界面集成的完整流程并参考其中的图表输出与排错思路快速完成自己的大作业或实战练习。1. 机票数据分析与可视化一份大作业怎么做出生产级质感机票价格是典型的时序数据波动剧烈、季节性明显、受节假日和航线竞争影响极大。很多同学拿到「机票数据分析与可视化」这个大作业题目时第一反应是找一份 CSVdf.describe()跑一遍再画几张柱状图和折线图交差。但真正拿高分的关键不在于你用了多花哨的图表而在于你有没有把「数据清洗 → 特征构造 → 多维分析 → 交互可视化」这条链路走完整并且每一步都有可解释的业务含义。这篇文章面向正在做 Python 数据分析与可视化课程大作业的读者也适合想用机票数据练手数据分析项目的人。我会按真实项目的推进顺序从数据获取与清洗讲起经过特征工程、多维聚合分析最后落到 ECharts 或 Pyecharts 的可视化大屏实现。中间会给出可直接复现的代码、参数说明以及我在做这类项目时踩过的坑。读完你应该能独立完成一份有分析深度、图表能讲故事的大作业而不是一堆散点图的堆砌。2. 数据获取与清洗机票数据从哪来、怎么洗2.1 数据来源的三种现实选择做机票分析第一步永远是数据。常见做法有三种第一种是使用公开数据集。Kaggle 上有不少航班价格数据集国内也有一些竞赛平台放出过脱敏的机票价格数据。这类数据的好处是字段规整、拿来即用缺点是时效性差往往是几年前的快照航线也以国外为主。第二种是自己采集。用 Python 爬虫抓取某程、某猪等平台的航班列表页能拿到实时价格、航班号、起降时间、机型、经停信息。但要注意这类页面普遍有反爬机制请求频率稍高就会触发验证码而且页面结构随时可能变。我一般会控制请求间隔在 3 到 5 秒并且只抓取公开可见的列表信息不涉及任何登录态数据。第三种是教师提供的教学数据集。很多课程会直接给一份 CSV 或 Excel字段包括出发城市、到达城市、起飞日期、价格、航空公司、舱位等。这是最省事也最稳妥的选择建议优先用这个。不管你用哪种来源拿到数据后的第一件事都是确认字段含义和数据类型。机票数据常见的字段结构如下字段名类型含义常见问题dep_citystring出发城市中英文混用、带空格arr_citystring到达城市同上dep_timestring/datetime起飞时间格式不统一pricefloat票价含货币符号、有空值airlinestring航空公司名称简写不一致cabinstring舱位等级缺失较多stopsint经停次数有时是文本「直飞」2.2 用 Pandas 做一轮标准化清洗下面这段代码是我做机票数据清洗时常用的模板覆盖了去重、类型转换、缺失值处理和异常值过滤import pandas as pd import numpy as np # 读取原始数据注意编码国内数据常见 gbk 或 utf-8-sig df pd.read_csv(flight_price_raw.csv, encodingutf-8-sig) # 1. 列名统一为小写下划线风格 df.columns [c.strip().lower().replace( , _) for c in df.columns] # 2. 去除完全重复的行 df df.drop_duplicates() # 3. 价格列清洗去掉货币符号和千分位逗号转为 float df[price] ( df[price] .astype(str) .str.replace(r[¥,], , regexTrue) .str.strip() ) df[price] pd.to_numeric(df[price], errorscoerce) # 4. 时间列统一转为 datetime df[dep_time] pd.to_datetime(df[dep_time], errorscoerce) # 5. 城市名去空格、统一为中文 df[dep_city] df[dep_city].astype(str).str.strip() df[arr_city] df[arr_city].astype(str).str.strip() # 6. 删除价格或时间为空的行 df df.dropna(subset[price, dep_time]) # 7. 过滤明显异常的价格比如 0 元或超过 5 万 df df[(df[price] 50) (df[price] 50000)] print(df.shape) print(df.dtypes)这段代码的逻辑很直接先把列名规范化方便后续引用然后处理价格列里的脏字符用errorscoerce把无法转换的值变成 NaN再统一删除时间列同理。最后用价格区间做一次粗过滤把明显是录入错误或测试数据的行去掉。参数方面价格下限我设的是 50 元因为国内航线即使促销也很少低于这个数上限 50000 元覆盖了头等舱和国际航线。如果你的数据里包含国际航班上限可以适当放宽。encoding参数要根据实际文件调整用utf-8-sig能兼容带 BOM 的 Excel 导出文件。注意清洗完一定要用df.info()和df.describe()再看一遍确认没有把正常数据误删。我见过有人把价格上限设成 5000结果所有国际航线全被过滤掉了。2.3 缺失值的处理策略机票数据里舱位和经停次数这两个字段缺失最严重。我的处理原则是如果缺失比例低于 5%直接删行如果在 5% 到 30% 之间用众数或「未知」填充超过 30% 就考虑整列丢弃或者在分析时明确标注「该维度数据不完整」。# 计算各列缺失比例 missing_ratio df.isnull().mean().sort_values(ascendingFalse) print(missing_ratio) # 对缺失比例低的列直接删行 low_missing_cols missing_ratio[missing_ratio 0.05].index df df.dropna(subsetlow_missing_cols) # 对中等缺失的类别列填充「未知」 for col in [cabin, airline]: if col in df.columns: df[col] df[col].fillna(未知)这里没有用均值填充价格因为价格是有业务含义的数值用均值填充会扭曲分布。如果确实需要保留缺失价格的行更合理的做法是用同航线同舱位的均价来填补但这会增加复杂度大作业里一般没必要。3. 特征工程把原始字段变成能分析的维度3.1 从时间字段里拆出分析维度原始数据只有一列dep_time但机票价格分析真正关心的是「星期几」「是不是节假日」「提前多少天买」。所以需要从时间字段里构造出这些衍生特征# 提取基础时间特征 df[dep_date] df[dep_time].dt.date df[dep_hour] df[dep_time].dt.hour df[dep_weekday] df[dep_time].dt.weekday # 0周一, 6周日 df[dep_month] df[dep_time].dt.month df[is_weekend] df[dep_weekday].isin([5, 6]).astype(int) # 星期几映射为中文方便图表展示 weekday_map {0: 周一, 1: 周二, 2: 周三, 3: 周四, 4: 周五, 5: 周六, 6: 周日} df[weekday_cn] df[dep_weekday].map(weekday_map) # 如果数据里有查询日期可以算提前购票天数 # df[days_ahead] (df[dep_date] - df[query_date]).dt.daysdt.weekday返回的是 0 到 6 的整数周一为 0。这个映射关系一定要记清楚否则图表上「周末高价」的结论可能正好反了。is_weekend用isin([5,6])判断比用 5更直观。如果你的数据里包含查询日期也就是用户搜索航班的那一天那days_ahead这个特征非常关键。它能回答「提前多久买票最便宜」这个经典问题。没有查询日期的话这个维度就做不了不要硬编。3.2 航线和航空公司的聚合特征单条航班记录的分析价值有限真正有洞察力的是聚合后的统计量。比如「每条航线的均价和价格波动」「每家航空公司的准点率与均价关系」# 按航线聚合计算均价、最低价、最高价和价格标准差 route_stats df.groupby([dep_city, arr_city]).agg( avg_price(price, mean), min_price(price, min), max_price(price, max), price_std(price, std), flight_count(price, count) ).reset_index() # 价格波动系数标准差除以均值衡量价格稳定性 route_stats[cv] route_stats[price_std] / route_stats[avg_price] # 按航空公司聚合 airline_stats df.groupby(airline).agg( avg_price(price, mean), flight_count(price, count), route_count(arr_city, nunique) ).reset_index().sort_values(avg_price, ascendingFalse)price_std是标准差反映价格离散程度cv是变异系数消除了量纲影响更适合跨航线比较。flight_count用来判断样本量是否足够如果某条航线只有两三条记录它的统计量参考价值就很低后续分析时应该过滤掉。提示聚合之后一定要检查flight_count样本量太小的航线在可视化时容易产生误导性的极值。我一般会设一个阈值比如少于 10 条记录的航线不纳入对比分析。3.3 价格分箱与标签化为了做分布分析和分类可视化可以把连续的价格切分成几个区间# 按分位数切分为低价、中价、高价、超高价四档 df[price_level] pd.qcut( df[price], q4, labels[低价, 中价, 高价, 超高价] ) # 查看各档的边界和数量 print(df.groupby(price_level)[price].agg([min, max, count]))用pd.qcut而不是pd.cut的好处是每档样本量大致相等避免某一档只有几条数据。q4是四分位如果你想分得更细可以改成 5 或 10。标签用中文是为了图表展示方便如果后续要喂给模型建议保留数值编码。4. 多维分析机票价格到底受什么影响4.1 时间维度星期几和月份的价格规律先看一周内不同出发日的均价差异weekday_price df.groupby(weekday_cn)[price].mean().reindex( [周一, 周二, 周三, 周四, 周五, 周六, 周日] ) print(weekday_price)从经验来看周五和周日的均价通常最高因为商务出行和周末返程叠加周二、周三往往最低。但这个规律不是绝对的具体要看你的数据覆盖的航线和时间段。如果你的数据里国际航线占比高规律可能又不一样。月份维度上春节、暑假、国庆所在的月份价格明显抬升。可以用箱线图来看每个月的价格分布比只看均值更有信息量因为均值会被极端高价拉偏。month_price df.groupby(dep_month)[price].describe() print(month_price[[mean, 50%, std]])50%就是中位数和均值对比能看出分布是否偏斜。如果均值远大于中位数说明有少量超高价航班拉高了整体水平。4.2 航线维度热门航线与价格洼地航线分析是机票数据里最有商业价值的部分。哪些航线均价最高、哪些航线价格波动最大、哪些航线航班最多这些都能直接支撑可视化图表# 取航班量最多的前 15 条航线 top_routes route_stats.nlargest(15, flight_count) # 取均价最高的前 10 条航线要求至少 10 个航班 high_price_routes ( route_stats[route_stats[flight_count] 10] .nlargest(10, avg_price) ) # 取价格波动最大的前 10 条航线 volatile_routes ( route_stats[route_stats[flight_count] 10] .nlargest(10, cv) )nlargest比sort_values().head()写起来更简洁效果一样。过滤flight_count 10是为了避免小样本偏差。cv最大的航线说明价格极不稳定可能是热门旅游线路或者竞争不充分的航线。4.3 航空公司维度均价与市场份额航空公司维度的分析可以做两个图一个是各航司的均价对比柱状图一个是航班量占比的饼图或环形图。# 计算各航司的市场份额 airline_stats[market_share] ( airline_stats[flight_count] / airline_stats[flight_count].sum() ) # 按均价排序输出 print(airline_stats.sort_values(avg_price, ascendingFalse))市场份额用flight_count占比来近似严格来说应该用座位数或收入但大作业里航班量已经够用了。如果数据里有舱位信息可以进一步拆分「经济舱均价」和「公务舱均价」对比会更公平。4.4 交叉分析航线 × 时间的热力矩阵把航线和星期几做交叉算均价就能得到一个热力矩阵非常适合用热力图展示# 取前 10 条热门航线 top10_routes route_stats.nlargest(10, flight_count) top10_pairs set(zip(top10_routes[dep_city], top10_routes[arr_city])) # 筛选并构造透视表 df_top df[df.apply(lambda r: (r[dep_city], r[arr_city]) in top10_pairs, axis1)] pivot df_top.pivot_table( valuesprice, index[dep_city, arr_city], columnsweekday_cn, aggfuncmean ) # 按周一至周日排序 pivot pivot[[周一, 周二, 周三, 周四, 周五, 周六, 周日]] print(pivot.round(0))pivot_table的aggfunc默认是均值这里显式写出来更清晰。apply那行是为了筛选出属于前 10 航线的记录数据量大时可以用 merge 替代效率更高。热力矩阵能一眼看出「哪条航线在星期几最贵」是可视化大屏里很出彩的一块。5. 可视化落地从 Matplotlib 到 Pyecharts 大屏5.1 静态图用 Matplotlib 快速验证在正式做可视化大屏之前我习惯先用 Matplotlib 快速画几张图验证分析结论因为它的 API 最直接调试成本低import matplotlib.pyplot as plt import matplotlib # 设置中文字体Windows 用 SimHeiMac 用 Arial Unicode MS matplotlib.rcParams[font.sans-serif] [SimHei] matplotlib.rcParams[axes.unicode_minus] False fig, axes plt.subplots(1, 2, figsize(14, 5)) # 左图各航司均价 airline_stats.sort_values(avg_price).plot( kindbarh, xairline, yavg_price, axaxes[0], color#4C72B0, legendFalse ) axes[0].set_title(各航空公司平均票价) axes[0].set_xlabel(均价元) # 右图一周价格趋势 weekday_price.plot( kindline, axaxes[1], markero, color#DD8452 ) axes[1].set_title(一周内出发日均价趋势) axes[1].set_ylabel(均价元) plt.tight_layout() plt.savefig(overview.png, dpi150) plt.show()中文字体那两行是必须的否则图表上的中文会变成方块。figsize控制画布大小dpi150保证保存的图片清晰度够用。barh是水平柱状图航空公司名字较长时比垂直柱状图更易读。5.2 用 Pyecharts 做交互式可视化大屏大作业如果想拿高分交互式图表比静态图更有优势。Pyecharts 是基于 ECharts 的 Python 封装能生成 HTML 文件支持鼠标悬停、缩放、图例切换。下面是一个组合大屏的骨架from pyecharts import options as opts from pyecharts.charts import Bar, Line, Pie, HeatMap, Grid, Page from pyecharts.commons.utils import JsCode # 1. 各航司均价柱状图 bar ( Bar() .add_xaxis(airline_stats[airline].tolist()) .add_yaxis(平均票价, airline_stats[avg_price].round(0).tolist()) .set_global_opts( title_optsopts.TitleOpts(title各航空公司平均票价), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate30)), yaxis_optsopts.AxisOpts(name元), datazoom_optsopts.DataZoomOpts(), ) ) # 2. 一周价格趋势折线图 line ( Line() .add_xaxis(weekday_price.index.tolist()) .add_yaxis(均价, weekday_price.round(0).tolist(), is_smoothTrue) .set_global_opts( title_optsopts.TitleOpts(title一周出发日均价趋势), yaxis_optsopts.AxisOpts(name元), ) ) # 3. 航司市场份额饼图 pie ( Pie() .add( , [list(z) for z in zip( airline_stats[airline].tolist(), airline_stats[flight_count].tolist() )], radius[30%, 55%], ) .set_global_opts(title_optsopts.TitleOpts(title航司航班量占比)) .set_series_opts(label_optsopts.LabelOpts(formatter{b}: {d}%)) ) # 4. 组合到一个页面 page Page(layoutPage.DraggablePageLayout) page.add(bar, line, pie) page.render(flight_dashboard.html)DataZoomOpts让柱状图支持缩放航司多的时候很实用。is_smoothTrue让折线更平滑但注意平滑折线可能掩盖真实波动如果数据点少建议关掉。饼图的radius设成环形比实心饼图更现代。Page把所有图表放在一个 HTML 里DraggablePageLayout允许拖拽调整布局方便你手动排版。5.3 热力图与地图的接入如果想把航线数据画在地图上Pyecharts 支持 Geo 和 Map 图表。但要注意地图需要城市经纬度数据Pyecharts 内置了一部分中国城市的坐标但并非全部。如果出发或到达城市不在内置列表里需要自己补充坐标from pyecharts.charts import Geo from pyecharts.globals import ChartType # 构造航线流量数据 route_flow ( df.groupby([dep_city, arr_city]) .size() .reset_index(namecount) .nlargest(20, count) ) geo ( Geo() .add_schema(maptypechina) .add( 航线, [list(z) for z in zip( route_flow[dep_city] - route_flow[arr_city], route_flow[count] )], type_ChartType.LINES, effect_optsopts.EffectOpts(symbolarrow, symbol_size6), ) .set_global_opts(title_optsopts.TitleOpts(title热门航线流向)) ) geo.render(route_map.html)ChartType.LINES会画出带箭头的流向线effect_opts控制箭头大小。如果城市名和 Pyecharts 内置的不一致比如「北京」和「北京市」地图上不会显示需要先做名称标准化。6. 避坑与排查机票数据分析里最容易翻车的五件事6.1 中文乱码图表和 CSV 各有一套解法现象Matplotlib 图表上中文显示为方块或者读取 CSV 时报UnicodeDecodeError。原因Matplotlib 默认字体不含中文CSV 文件的编码可能是 GBK、GB2312 或带 BOM 的 UTF-8。解决图表端设置matplotlib.rcParams[font.sans-serif] [SimHei]Windows或[Arial Unicode MS]Mac并加上axes.unicode_minus False解决负号显示问题。读取端先试utf-8-sig失败再试gbk可以用chardet库自动检测编码。6.2 日期解析失败格式不统一是常态现象pd.to_datetime报错或者解析出来的日期全是 NaT。原因同一列里混了「2024-01-01」「2024/1/1」「01-01-2024」等多种格式。解决先用errorscoerce把无法解析的变成 NaT统计 NaT 比例。如果比例高用format参数指定格式分批解析或者用dateutil.parser逐行解析。不要直接dropna先看看是哪些行出了问题。6.3 聚合结果被小样本带偏现象某条航线均价 8000 元点进去发现只有 2 条记录其中一条是头等舱。原因没有对聚合后的样本量做过滤。解决所有聚合分析都加一个flight_count阈值低于 10 条的航线或航司不纳入对比。在图表上用标注说明「仅展示样本量 ≥ 10 的航线」。6.4 Pyecharts 图表不显示或空白现象生成的 HTML 打开后一片空白或者图表区域是白的。原因常见的有三种——数据里有 NaNPyecharts 无法序列化城市名和内置地图不匹配Page布局里图表顺序或参数写错。解决渲染前用df.fillna(0)或df.dropna()处理数据用print确认传给图表的 list 里没有 NaN地图类图表先检查城市名是否在pyecharts.datasets的坐标表里。6.5 价格分析忽略了舱位差异现象得出「某航司最贵」的结论但实际上该航司公务舱占比高经济舱反而便宜。原因没有按舱位分层分析。解决如果数据有舱位字段所有价格对比都要先按舱位分组至少区分经济舱和公务舱。如果舱位缺失严重在结论里明确写「未区分舱位结论仅供参考」。7. 让大作业脱颖而出的一个技巧用价格日历做「最佳购票窗口」分析大部分机票分析作业做到「各维度均价对比」就停了但有一个分析角度既简单又有冲击力价格日历。它的核心思路是把「出发日期」和「提前购买天数」做交叉算出每个格子里的最低价然后找出「提前几天买、星期几出发」的组合最优解。具体做法是如果你的数据里有查询日期字段先算出days_ahead 出发日期 - 查询日期然后按days_ahead分箱比如 0-3 天、4-7 天、8-14 天、15-30 天、30 天以上再和weekday_cn做透视# 假设数据里有 query_date 字段 df[days_ahead] (df[dep_date] - df[query_date]).dt.days # 只保留合理的提前天数 df_cal df[(df[days_ahead] 0) (df[days_ahead] 60)].copy() # 分箱 bins [0, 3, 7, 14, 30, 60] labels [0-3天, 4-7天, 8-14天, 15-30天, 30天以上] df_cal[ahead_bin] pd.cut(df_cal[days_ahead], binsbins, labelslabels) # 透视提前天数 × 星期几的最低价 calendar df_cal.pivot_table( valuesprice, indexahead_bin, columnsweekday_cn, aggfuncmin ) calendar calendar[[周一, 周二, 周三, 周四, 周五, 周六, 周日]] print(calendar.round(0))这个矩阵直接回答了一个所有人都关心的问题什么时候买、什么时候飞最便宜。用aggfuncmin而不是mean是因为对购票者来说最低价才是真正可执行的决策依据。如果你想让图表更直观可以用 Pyecharts 的 HeatMap 把这个矩阵画出来颜色越深代表价格越低一眼就能看出「最优购票窗口」。没有查询日期字段的话这个分析做不了不要硬编。但你可以退而求其次用「出发月份 × 星期几」做类似的热力矩阵虽然不如提前天数精确但也能看出季节性规律。我自己做这类项目最大的教训是不要一上来就追求图表数量先把两三个核心分析做深做透每个图表都能回答一个具体问题比堆二十张图更有说服力。老师看的是你的分析逻辑不是你的绘图工作量。希望帮到你。本文还有配套的精品资源点击获取