简介:这份资源是面向计算机相关专业学生与项目实战学习者的Python数据分析可视化大作业完整方案,以机票价格数据分析与预测为核心,结合PyQt5实现可视化界面展示,适合正在准备课程设计、期末大作业或需要数据分析练手项目的人群。压缩包共37个文件,约1.33MB,包含11个py源码文件、9个csv数据集、6个xml配置、6张png图表、2个ui界面文件及说明文档,覆盖数据预处理、回归分析、回归可视化、数据可视化等模块,目录结构清晰,便于按功能检索学习。目前已有402人学习下载。项目源自大三期末作业,经导师指导认可,评审分99分,代码完整可运行,对新手友好。读者可据此掌握从原始数据清洗到价格预测建模、再到PyQt5图形界面呈现的完整流程,理解各分析环节的实现思路与图表输出方式,也可作为课程设计参考模板进行二次开发与功能扩展。
1. 机票数据分析与可视化大作业:一份能直接跑通的 Python 项目拆解
期末大作业最怕的不是不会写代码,而是拿到一份源码跑不起来、数据对不上、图表丑得没法交。这份「Python数据分析可视化大作业-机票数据分析与可视化」就是冲着这个痛点来的:它把一份机票数据集从清洗、特征提取到多维可视化串成完整链路,用 pandas 做数据处理、matplotlib 和 seaborn 出图,适合正在赶 Python 数据分析期末大作业的学生,也适合想拿一个真实业务场景练手数据分析项目的人。我拆过不少同类源码包,很多是「能看不能跑」,这份的目录结构和依赖相对干净,下面按「它是什么 → 怎么跑起来 → 参数怎么调 → 坑在哪」的顺序讲透,让你拿到手就能复现,而不是对着报错发呆。
2. 环境准备与数据加载:把源码跑起来的第一步
2.1 依赖清单与 Python 环境配置
拿到源码包后别急着双击运行,先确认环境。这类数据分析大作业通常依赖 pandas、numpy、matplotlib、seaborn 四个核心库,部分版本还会用到 jupyter notebook 做交互式调试。我一般会先建一个独立虚拟环境,避免和系统里已有的库版本打架——这是血泪经验,pandas 2.x 和 1.x 在部分 API 上不兼容,直接全局装很容易翻车。
# 创建虚拟环境(Python 3.8+ 均可,推荐 3.9/3.10) python -m venv flight_env # 激活环境:Windows flight_env\Scripts\activate # 激活环境:macOS / Linux source flight_env/bin/activate # 安装核心依赖 pip install pandas numpy matplotlib seaborn jupyter这段命令做了三件事:建隔离环境、激活、装库。参数上注意python -m venv后面的目录名可以自定义,激活命令在 Windows 和 macOS 下不一样,这是新手最容易卡住的地方。装完后用pip list确认版本,pandas 建议 1.5 以上,matplotlib 3.5 以上,seaborn 0.12 以上,太低会导致部分绘图函数报AttributeError。
如果你用 VS Code 写代码,记得在右下角把解释器切到刚建的flight_env,否则终端装好了、编辑器里还是报ModuleNotFoundError,这个坑我见过太多人踩。Jupyter 用户则要在 notebook 里执行import sys; sys.executable确认内核路径一致。
2.2 数据文件定位与读取参数
源码包里通常有一个data目录,里面放着 CSV 或 Excel 格式的机票数据。读取时最容易出问题的是编码和列名。国内数据集常见gbk或utf-8-sig编码,直接pd.read_csv默认 utf-8 会报UnicodeDecodeError。
import pandas as pd # 读取机票数据,注意编码和分隔符 df = pd.read_csv( 'data/flight_data.csv', encoding='utf-8-sig', # 中文列名常见编码,避免乱码 sep=',', # 分隔符,若为制表符改成 '\t' parse_dates=['flight_date'] # 直接把日期列解析成 datetime ) # 快速体检:看形状、列名、缺失情况 print(df.shape) print(df.columns.tolist()) print(df.isnull().sum())encoding参数是中文数据集的第一道坎,utf-8-sig能处理带 BOM 的文件,gbk适合老版 Excel 导出的 CSV。parse_dates把日期列一次性转成时间类型,后面做按月、按周聚合时不用再手动转换,省一步就少一个 bug。isnull().sum()是必做的体检,机票数据里价格、航班号、起降时间经常有缺失,先看清楚再决定是删还是填。
如果数据是 Excel 格式,换成pd.read_excel('data/flight_data.xlsx', sheet_name=0),sheet_name指定工作表,多表时别读错。读取后建议先df.head()和df.info()各看一遍,确认列类型符合预期——价格列如果是 object 类型,说明混进了货币符号或空字符串,得先清洗。
3. 数据清洗与特征工程:让机票数据能算能画
3.1 缺失值、异常值与重复行处理
原始机票数据几乎不可能干净。常见脏数据有三类:价格为空或为 0、起降时间缺失、同一航班重复记录。处理策略要按业务含义来,不能无脑dropna。
# 1. 删除完全重复的行 df = df.drop_duplicates() # 2. 价格列:转数值,剔除 0 和极端异常值 df['price'] = pd.to_numeric(df['price'], errors='coerce') df = df[df['price'].between(100, 20000)] # 按业务合理区间过滤 # 3. 关键字段缺失直接删,非关键字段填充 df = df.dropna(subset=['flight_date', 'departure', 'arrival']) df['airline'] = df['airline'].fillna('未知航司') # 4. 重置索引,避免后续绘图索引错乱 df = df.reset_index(drop=True)pd.to_numeric配合errors='coerce'能把无法转换的值变成 NaN,比直接astype(float)安全得多,后者遇到脏字符直接抛异常。价格区间100~20000是经验值,你可以根据数据集实际分布调整,目的是剔除录入错误的天价票或 0 元票。dropna的subset参数只检查关键列,避免因为一个无关字段缺失就丢掉整行有效数据。最后reset_index很关键,删行后索引会断,后面用iloc或绘图时容易对不上号。
3.2 时间维度与航线特征提取
机票分析的核心维度是时间和航线。把日期拆成年、月、周、是否周末,把起降城市拼成航线,这些衍生特征直接决定后面图表能讲出什么故事。
# 时间特征 df['year'] = df['flight_date'].dt.year df['month'] = df['flight_date'].dt.month df['weekday'] = df['flight_date'].dt.dayofweek # 0=周一 df['is_weekend'] = df['weekday'].isin([5, 6]).astype(int) # 航线特征:出发-到达拼接 df['route'] = df['departure'] + '-' + df['arrival'] # 折扣率:假设原价列存在 if 'original_price' in df.columns: df['discount'] = df['price'] / df['original_price'] # 按航线统计均价,方便后续对比 route_avg = df.groupby('route')['price'].agg(['mean', 'count']).reset_index() route_avg = route_avg[route_avg['count'] >= 10] # 样本太少的航线不参与分析dt.dayofweek返回 0 到 6,周一为 0,用isin([5,6])标记周末,比字符串判断快且不易错。航线拼接用+即可,但如果城市名有空格,先str.strip()处理。groupby后加count过滤是为了避免「某航线只有 1 条记录却均价极高」这种统计噪声,样本量阈值 10 是常见做法,你可以按数据量调整。这一步做完,数据就从「一张表」变成了「可多维分析的宽表」,后面所有图表都从这里出发。
4. 可视化实战:五类图表与参数调优
4.1 月度票价趋势与航司对比
趋势图是机票分析的门面,通常用折线图看月度均价变化,用柱状图对比航司。matplotlib 默认样式偏丑,调几个参数就能上一个档次。
import matplotlib.pyplot as plt import seaborn as sns # 全局样式:中文字体 + 分辨率 plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows 用黑体 plt.rcParams['axes.unicode_minus'] = False # 负号正常显示 sns.set_style('whitegrid') # 月度均价趋势 monthly = df.groupby('month')['price'].mean().reset_index() plt.figure(figsize=(10, 5), dpi=120) plt.plot(monthly['month'], monthly['price'], marker='o', linewidth=2, color='#2E86AB') plt.title('各月平均票价趋势', fontsize=14) plt.xlabel('月份') plt.ylabel('平均票价(元)') plt.xticks(range(1, 13)) plt.tight_layout() plt.savefig('output/monthly_price.png', bbox_inches='tight') plt.show()font.sans-serif设成SimHei是 Windows 下的常规操作,macOS 换成Arial Unicode MS,Linux 服务器没有中文字体时要么装字体要么用英文标签,否则中文全变方块——这是最经典的翻车现场。axes.unicode_minus=False解决负号显示成方块的问题。figsize和dpi一起控制输出清晰度,dpi=120在报告里够用,要打印就上 300。bbox_inches='tight'防止保存时标签被裁掉,这个小参数很多人不知道,导致图存下来缺一块。
航司对比用横向柱状图更易读,航司名长时不会挤在一起:
airline_avg = df.groupby('airline')['price'].mean().sort_values(ascending=True) plt.figure(figsize=(10, 6), dpi=120) airline_avg.plot(kind='barh', color='#A23B72') plt.title('各航司平均票价对比', fontsize=14) plt.xlabel('平均票价(元)') plt.tight_layout() plt.savefig('output/airline_price.png', bbox_inches='tight') plt.show()sort_values(ascending=True)让柱状图从低到高排列,视觉上更有层次。barh是横向柱,适合类别名较长的场景。颜色用十六进制而不是默认色,报告观感会好很多,这是低成本提分点。
4.2 航线热度与价格分布
航线分析通常要回答两个问题:哪些航线最热、价格分布是否集中。热力图和箱线图是标配。
# 热门航线 Top10 top_routes = df['route'].value_counts().head(10) plt.figure(figsize=(10, 5), dpi=120) sns.barplot(x=top_routes.values, y=top_routes.index, palette='viridis') plt.title('热门航线 Top10', fontsize=14) plt.xlabel('航班数量') plt.tight_layout() plt.savefig('output/top_routes.png', bbox_inches='tight') plt.show() # 价格分布箱线图(按舱位或航司) plt.figure(figsize=(12, 6), dpi=120) sns.boxplot(data=df, x='airline', y='price', palette='Set2') plt.xticks(rotation=45) plt.title('各航司票价分布', fontsize=14) plt.tight_layout() plt.savefig('output/price_box.png', bbox_inches='tight') plt.show()value_counts().head(10)一行拿到最热航线,sns.barplot的palette参数换配色方案,viridis和Set2都是对色盲友好的选择。箱线图能同时看中位数、四分位和离群点,比只看均值信息量大得多。rotation=45防止航司名重叠,这个参数在类别多时必加。如果箱线图离群点太多导致主体被压扁,可以加showfliers=False隐藏离群点,但报告里最好保留并说明,否则有掩盖异常之嫌。
4.3 时间热力图与周末效应
把「月份 × 星期」做成热力图,能一眼看出出行高峰和价格洼地,这是机票分析里比较出彩的一张图。
# 透视表:行=月份,列=星期,值=均价 pivot = df.pivot_table( values='price', index='month', columns='weekday', aggfunc='mean' ) plt.figure(figsize=(10, 6), dpi=120) sns.heatmap(pivot, cmap='YlOrRd', annot=True, fmt='.0f', linewidths=0.5) plt.title('月份-星期平均票价热力图', fontsize=14) plt.xlabel('星期(0=周一)') plt.ylabel('月份') plt.tight_layout() plt.savefig('output/heatmap.png', bbox_inches='tight') plt.show()pivot_table的aggfunc='mean'决定格子里的值,换成count就是航班量热力图。annot=True把数值标在格子里,fmt='.0f'控制不显示小数,否则格子会挤。cmap='YlOrRd'是黄到红的渐变,价格越高越红,直觉上容易理解。这张图能直接支撑「几月几号买票贵」的结论,是大作业里拉开差距的地方。
5. 避坑与常见问题排查
5.1 中文乱码与负号显示异常
现象:图表标题、轴标签里的中文变成一个个方块,负号也显示成方块。原因:matplotlib 默认字体不含中文,且默认用 Unicode 负号。解决:在绘图前统一设置plt.rcParams['font.sans-serif']和plt.rcParams['axes.unicode_minus'] = False。Windows 用SimHei,macOS 用Arial Unicode MS,Linux 需先fc-list :lang=zh确认已装中文字体,没有就装fonts-noto-cjk并重建字体缓存。这个设置要放在所有绘图代码之前,放后面不生效。
5.2 日期解析失败导致 dt 访问器报错
现象:执行df['flight_date'].dt.month时报AttributeError: Can only use .dt accessor with datetimelike values。原因:该列还是 object 字符串类型,没被解析成 datetime。解决:读取时加parse_dates=['flight_date'],或读完后df['flight_date'] = pd.to_datetime(df['flight_date'], errors='coerce')。加errors='coerce'能把无法解析的值变 NaT,避免整列转换失败。转换后再dropna(subset=['flight_date']),否则后面聚合会报错。
5.3 分组聚合后索引错位
现象:groupby之后直接绘图,x 轴标签对不上,或者reset_index忘了加导致列名变成索引。原因:pandas 的groupby默认把分组键设为索引,绘图时容易混淆。解决:聚合后统一.reset_index(),把分组键变回普通列。如果用了agg多函数,列会变成 MultiIndex,用route_avg.columns = ['route', 'avg_price', 'count']重命名,或者route_avg.columns = route_avg.columns.droplevel(0)处理。这个坑不报错但结果错,最隐蔽。
5.4 保存图片空白或标签被裁
现象:plt.savefig存出来的图是空白,或者边缘标签缺一半。原因:savefig在plt.show()之后执行,此时画布已清空;或者没加bbox_inches='tight'。解决:把savefig放在show之前,并加bbox_inches='tight'。Jupyter 里show会清空当前 figure,顺序反了就是白图。另外tight_layout()要在savefig前调用,否则布局调整不生效。
5.5 样本量过小导致统计失真
现象:某航线均价 8000 元,点进去发现只有 2 条记录。原因:分组后没过滤小样本,个别极端值拉高均值。解决:groupby后加count列,过滤count >= 10再分析。阈值按数据总量定,数据少就降到 5,但要在报告里说明。这个坑在航线、航司维度都常见,不做过滤结论就不可信。
6. 进阶技巧:把静态图升级成可交互看板
大作业如果只交几张 PNG,分数上限有限。把核心图表用 pyecharts 或 plotly 做成 HTML 交互看板,鼠标悬停显示数值、图例可点击筛选,观感和实用性都上一个台阶。我一般用 pyecharts,中文文档友好,导出单文件 HTML 直接交。
from pyecharts.charts import Line, Bar, Page from pyecharts import options as opts # 月度均价折线 line = ( Line() .add_xaxis(monthly['month'].astype(str).tolist()) .add_yaxis('平均票价', monthly['price'].round(0).tolist(), is_smooth=True) .set_global_opts( title_opts=opts.TitleOpts(title='月度票价趋势'), tooltip_opts=opts.TooltipOpts(trigger='axis'), xaxis_opts=opts.AxisOpts(name='月份'), yaxis_opts=opts.AxisOpts(name='票价(元)') ) ) # 航司对比柱状 bar = ( Bar() .add_xaxis(airline_avg.index.tolist()) .add_yaxis('均价', airline_avg.round(0).tolist()) .set_global_opts( title_opts=opts.TitleOpts(title='航司均价对比'), xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=30)) ) ) # 组合成一个页面导出 page = Page(layout=Page.SimplePageLayout) page.add(line, bar) page.render('output/flight_dashboard.html')is_smooth=True让折线平滑,trigger='axis'让提示框跟随 x 轴联动,rotate=30解决航司名重叠。Page把多张图拼成一个 HTML,render导出后双击就能在浏览器打开,不需要服务器。参数上round(0)把浮点票价取整,避免提示框里一长串小数。如果要做地图航线图,pyecharts 的Geo或Map组件可以叠加航线,但需要城市经纬度,数据里没有的话得自己补一份对照表,这是额外工作量,按时间决定要不要做。
验证看板是否正常,重点看三处:中文是否正常显示(pyecharts 自带字体处理,一般没问题)、数据是否和 pandas 算出来的一致(随机抽一个月的均价手工核对)、导出文件能否离线打开(不依赖网络 CDN)。我习惯在render后加一句print('看板已导出'),确认流程走完。从那以后我每次交数据分析项目,都会先跑一遍完整流程再打包,避免「本地能跑、换台机器就崩」的尴尬。希望帮到你。
本文还有配套的精品资源,点击获取