ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python电影票房数据分析与预测:从爬虫到机器学习的毕设指南

Python电影票房数据分析与预测:从爬虫到机器学习的毕设指南 简介面向电影数据可视化与票房影响因素分析预测的Python毕业设计项目适合计算机、数据科学相关专业学生作为课题参考也适合对爬虫、数据库、数据建模感兴趣的开发者作为练习项目。该方案以豆瓣电影数据为对象从数据采集入库、SQL查询、Pandas可视化到票房预测完整覆盖一个数据分析类毕业设计的核心环节Jupyter Notebook逐段组织代码便于一边运行一边理解清洗、建模、评估的每个步骤也方便后续替换成自己的数据集。包内共38个文件包括24张可视化结果图票房分布、预测对比、数据库结构等、6个Python脚本、3个Notebook文件、1份PDF文档和1个SQL脚本整体仅5.17MB结构清晰且轻量。已有335人学习下载项目经过导师指导与严格调试核心脚本和依赖说明齐全能帮助开发者节省从零搭建的时间快速拿下一套可演示、可扩展的毕业设计源码。1. 别把毕设做成爬虫展示电影票房项目到底能交付什么如果你正在找一个能写进简历、能过答辩的 Python 方向项目这部电影数据可视化及票房影响因素分析与预测的源码包大概率已经在你的下载列表里躺了一段时间了。这是一个标准的数据分析流水线电影数据可视化把是什么讲清楚票房影响因素分析回答为什么票房预测则把历史规律外推到接下来会怎样。zip 里的源码对应能跑的代码PDF 文档对应能答辩的论文两者合起来才是一个完整的毕业设计交付物。适合谁适合已经会 Python 基础语法、想找一个能落到真实数据集的练手或毕设方向但不想只交一个爬虫脚本的人。这类项目的难点从来不在爬虫而在爬下来的数据能不能用来分析、分析结论能不能自洽、模型预测结果能不能解释。所以下文我会按一个完整项目的推进顺序把数据采集、清洗、可视化、建模和避坑一条龙讲清楚每一段都给出可以直接复现的代码和参数方便你照着做或者改造成自己的毕设。2. 数据从哪来requests BeautifulSoup 抓取电影榜单与票房字段电影数据可视化和票房预测项目数据是整个链条的地基。常见做法是去公开的电影网站抓取榜单数据比如猫眼、豆瓣的公开页面或公开接口。抓下来之后还要经过清洗和特征工程才能真正进入分析和建模环节。2.1 先想清楚采集哪些字段再写爬虫很多初学者一上来就写爬虫抓回一堆 HTML 再开始想这些够不够这是典型的反着做。我的习惯是先列一张表把分析和建模需要的字段写清楚再去对页面结构。字段名类型示例用途titlestr流浪地球2唯一标识 / 主键directorstr郭帆导演影响力分析actorsstr吴京 / 刘德华主演热度特征genresstr科幻 / 冒险类型特征release_datedate2023-01-22档期特征box_officefloat402300万亿元预测目标ratingfloat8.3口碑特征vote_countint128000热度特征durationint173时长特征countrystr中国产地特征budgetfloat11投入特征is_holidayint1档期特征is_weekendint1档期特征这是一个典型的毕设级字段清单。字段并不是越多越好——预算这类字段虽然业界常用但如果历史数据缺失严重最后可能不得不整列删除。我倒建议是如果要用预算特征就把缺失行全部剔除而不是用均值填充才能保证模型的可靠性。爬虫部分的核心不是把字段抓全而是保证字段之间不发生混淆片名是片名票房是票房、评分是评分三者不能串列。如果你的数据里有同名电影比如《花木兰》在中美两国上映过两个版本要用title release_date组合来去重而不是只用片名。这看似小事却能避免后期同一部电影既出现在训练集又出现在测试集的数据泄漏。2.2 requests 请求头、超时与重试反爬规避的三个基础参数用 requests 抓取一个电影榜单页代码可以短到只有三五行但能不能稳定跑完才是关键。真实项目里没人会写打开页面 - 找到数据 - 关闭页面那种一步到位脚本因为网站服务器的访问频率限制、临时网络抖动、页面结构改版都会让你的脚本在跑到第 200 页时直接崩溃。import requests import time import random def fetch_page(url, max_retry3): session requests.Session() session.headers.update({ User-Agent: (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36), Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9, Referer: https://maoyan.com/, }) for attempt in range(1, max_retry 1): try: resp session.get(url, timeout(3, 10)) if resp.status_code 200: return resp elif resp.status_code 403 or resp.status_code 418: print(f第 {attempt} 次被拦截HTTP {resp.status_code}) time.sleep(5 * attempt random.uniform(0, 2)) else: print(fHTTP {resp.status_code}稍后重试) time.sleep(2) except requests.exceptions.Timeout: print(f第 {attempt} 次请求超时) time.sleep(3) return None这块代码里三个参数值得单独说。timeout(3, 10)是连接超时和读取超时不设超时的时候某个请求卡住整个脚本就挂在那里尤其抓取几百个页面时概率极高。连接超时定了 3 秒意思是 3 秒内连不上就断开读取超时定了 10 秒意思是连接建立后 10 秒内收不到完整响应也断开。max_retry3是重试次数对待反爬和网络抖动重试是常规操作因为网络超时往往一次就够而反爬拦截通常要等几秒再试。所以重试间隔用了5 * attempt random.uniform(0, 2)第一次被拦截等 5 秒第二次等 10 秒依次递增并且加了随机抖动避免重试请求也变成定时器式访问。session.headers.update设置的是会话级请求头整个爬取过程只需要设置一次。注意 User-Agent 必须用完整的浏览器版本不能简写成python-requests否则很多网站直接拒绝服务。Referer设为网站首页是因为部分后端会校验请求来源漏了这一项可能拿不到完整 HTML。如果用了网页登录态还要把Cookie也加进去否则只能访问到公开榜单拿不到完整的电影详情。写爬虫还有一个原则把请求封装成函数而不是在循环里堆session.get。后续抓 1000 页只需要调 1000 次fetch_page如果中途被封打断继续跑也比改一堆散乱代码容易。这种封装习惯在项目后期换数据源时能省下一大笔时间。2.3 解析 HTML 构造 DataFrame选择器比正则更抗改版页面抓回来是 HTML需要一个解析器。BeautifulSoup 是起步最稳妥的选择它的性能虽然在百万行级别不如 lxml 直接操作但在这类几千部电影的数据量下完全够用而且 API 简单新手不容易写错。from bs4 import BeautifulSoup import pandas as pd def parse_movie_page(html): soup BeautifulSoup(html, html.parser) rows [] # 榜单页里每个电影卡片对应一个 .movie-item 容器 for item in soup.select(.movie-item): title_tag item.select_one(.movie-title) if title_tag is None: continue title title_tag.get_text().strip() info_tag item.select_one(.movie-info) info_text info_tag.get_text().strip() if info_tag else box_office_tag item.select_one(.movie-box_office) box_office box_office_tag.get_text().strip() if box_office_tag else 0 rows.append({ title: title, info: info_text, box_office: box_office, }) return pd.DataFrame(rows)这里把每个.movie-item当作一个卡片处理选择器定位到具体字段。为什么用选择器而不是正则因为页面结构改版时选择器通常还能凑合用正则基本上就要重写而且先选卡片、再查字段的方式让解析逻辑一目了然后期调试也更方便。.select_one只取匹配到的第一个元素适合单值字段如果要取多个元素比如主演列表要改用.select后遍历。解析完成后紧接着在 pandas 里做基础去重和删除空行df.drop_duplicates(subsettitle, keepfirst)和df.dropna(subset[box_office])。数据量小的时候这些操作几乎不耗时数据量大到百万行再考虑转用多进程或分布式但这个毕设场景不会走到那一步。3. 清洗、特征工程与可视化把脏数据变成能建模的 CSV爬虫跑完你以为拿到的是数据实际上拿到的是一堆文本垃圾。中文空格、全角符号、繁体数字、万/亿混用这些才是电影数据可视化项目里真正磨人的部分。数据清洗没有高大上的模型可以依赖靠的是细心和习惯。3.1 pandas 清洗三板斧缺失值、重复值、类型转换整理清洗代码不能说复杂但要按顺序来先去重再处理缺失再转类型。顺序一换后面排查问题时会更绕。import pandas as pd df pd.read_csv(movie_raw.csv) # 1) 去重同一部电影可能在榜单里出现多次 df df.drop_duplicates(subsettitle, keepfirst).copy() # 2) 缺失值核心数值字段缺失直接删除避免用 0 填充而造成误导 df df.dropna(subset[box_office, rating]) # 3) 票房字段可能是 12.5万、1.2亿、8562.3万先统一成万元 def parse_box_office(text): text str(text).strip() if 亿 in text: return float(text.replace(亿, )) * 10000 if 万 in text: return float(text.replace(万, )) return float(text) df[box_office] df[box_office].map(parse_box_office) # 导演/主演/类型允许为空用空字符串占位不参与数值计算 df[director] df[director].fillna() df[actors] df[actors].fillna() df[genres] df[genres].fillna()每一步都有原因不先去掉重复同一个样本既在训练集又在测试集出现等于开卷考试不删缺失值后续LinearRegression直接报ValueError票房单位不统一模型预测的数值可能差一个数量级。清洗顺序也有讲究——先转类型再dropna缺失值虽然也能被识别但调试链路不够清爽。清洗完加一个快速校验assert df[box_office].dtype float assert df[box_office].isna().sum() 0 print(df.shape)这一步能帮你提前发现隐性问题。比如box_office虽然显示是 float但如果混入 NaNisna().sum()会立刻暴露。df.shape的变化也要心里有数原始数据 3000 条清洗后剩下 2500 条这个比例正常如果直接砍掉一半那就要检查是不是去重字段设错了。3.2 特征工程档期、类型、主演热度怎么变成数值模型只能吃数字特征工程就是把上映档期、类型、主演这类文本信息转成数值这一步的决策直接影响机器学习算法的表现上限。模型预测票房是一个回归任务但如果输入里只有票房和评分两个字段模型就只会在评分高票房高这条线上打转完全没有泛化能力。所以特征工程要能把数据里隐藏的规律显式化。# 1) 上映档期特征是否周末、是否节假日 df[release_date] pd.to_datetime(df[release_date], errorscoerce) df[is_weekend] df[release_date].dt.weekday.isin([4, 5]).astype(int) holiday_list {2023-01-21, 2023-01-22, 2023-10-01, 2023-10-02} df[is_holiday] df[release_date].astype(str).isin(holiday_list).astype(int) df[release_month] df[release_date].dt.month # 2) 类型多标签编码剧情/喜剧拆成多个布尔列 for t in [剧情, 喜剧, 科幻, 动作, 动画, 悬疑]: df[ftype_{t}] df[genres].str.contains(t, regexFalse).astype(int) # 3) 主演热度简单的演员-热度字典映射 actor_heat {吴京: 95, 沈腾: 92, 刘德华: 88, 易烊千玺: 90} df[actor_heat] df[actors].map( lambda x: actor_heat.get(str(x).split(/)[0].split( )[0].strip(), 0) )特征工程的水很深但毕设做到人无我有就够了档期特征、类型编码、主演热度这三类加进去模型效果立刻比裸数值字段好不少。这里有一个容易被忽视的细节release_month只有 12 个取值适合看作分类特征如果你想感知春节档暑期档这类更细的档期概念可以单独构造season列而不是简单用月份代替。比如把 1-2 月划为春节档、7-8 月划为暑期档、国庆节所在周划为国庆档一个 if-else 就能搞定。主演热度字典不必手动穷举真实项目里一般用演员主页的粉丝量、作品累计票房或豆瓣参评人数来近似。这个特征缺失是常态缺失时就填 0对树模型不会有太大影响但要注意别用演员热度去拟合训练集标签否则又变成信息泄漏。判断标准很简单如果这个特征只在电影上映后才产生那它不能用于上映前票房预测。3.3 matplotlib 与 pyecharts 出图答辩时图比表格更有说服力数据可视化是这个标题的门面模型可以简单但图必须有结论。评审老师看 PPT 最先扫图一张图能说明一个问题不要一张图塞十几个指标。如果提交的 PDF 文档里全是表格和文字答辩效果会大打折扣。经典组合是票房 Top20 柱状图、评分-票房散点图、年度票房趋势折线图、类型票房占比玫瑰图。前两种图就能直接支撑论文的核心结论。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False # 负号显示为方块的问题 plt.figure(figsize(10, 6)) plt.scatter(df[rating], df[box_office], alpha0.6) plt.xlabel(豆瓣评分) plt.ylabel(总票房万元) plt.title(电影评分与票房散点图) plt.show()两个参数必须记住axes.unicode_minus不设坐标轴的负号显示成方块font.sans-serif不设所有中文变成口口口。像这种图上如果能看到右上角有零星高票房高分片右下角有低分高票房片就能直接支撑评分高不一定票房高档期和热度同样重要的结论。如果想做交互式图表可以用 pyechartsfrom pyecharts.charts import Bar from pyecharts import options as opts top10 df.nlargest(10, box_office) bar ( Bar() .add_xaxis(top10[title].tolist()) .add_yaxis(票房, top10[box_office].round(2).tolist()) .set_global_opts(title_optsopts.TitleOpts(title票房 Top10)) ) bar.render(top10_bar.html)pyecharts 生成的是独立 HTML 文件双击就能在浏览器里看答辩时可以随手演示比静态 PNG 显得更用心。nlargest(10, box_office)返回的是票房前 10 行.tolist()用来把 pandas 的 Series 转成列表避免 pyecharts 在渲染时出现类型不匹配的警告。如果想把 Top10 柱状图按票房从高到低排序直接top10.sort_values(box_office, ascendingTrue)再传给add_xaxis图上的柱子就是从左到右递增的排列。4. 票房影响因素分析与预测建模相关性、模型对比与调参这一章是论文的核心章节也是答辩时被问最多的部分。分析过程要做出结论预测模型要能站得住脚。4.1 相关矩阵与对数变换别让高票房电影带偏结论第一步不是建模而是用相关性分析看看各特征与票房有多相关。常见做法是直接df.corr()但票房变量往往是右偏分布少数高票房电影会把相关性拉得很高引发误导。如果直接拿原始票房去算相关系数几部五十亿级别的电影会主导整个计算结果让评委质疑你的分析是否严谨。import numpy as np import seaborn as sns import matplotlib.pyplot as plt df[box_office_log] np.log1p(df[box_office]) cols [box_office_log, rating, vote_count, duration, is_weekend, is_holiday, actor_heat] corr df[cols].corr() plt.figure(figsize(8, 6)) sns.heatmap(corr, annotTrue, cmapRdBu_r, center0) plt.title(票房影响因子相关系数矩阵) plt.show()np.log1p是log(1x)好处是即使x0也不会算出负无穷。相关矩阵热力图上的数值能直观看出评分、参评人数、主演热度对票房都有一定正向相关性而片长、是否周末往往相关性很弱——这本身就是可以写进论文的分析结论。annotTrue会在每个格子里显示相关系数值答辩时不必刻意去读色块直接念数字就可以。这里要提醒一句相关系数只刻画线性关系它说评分与票房正相关不代表评分高导致票房高。如果答辩时把这个因果性说死评委大概率会追问一句那评分低的烂片为什么还有高票房场面就会变得尴尬。更严谨的表述是评分与票房存在中等程度的正相关相关系数约 0.52但在低分区间仍然存在高票房样本说明档期与主演热度对票房有独立增量贡献。4.2 线性回归 / 随机森林 / XGBoost 三模型对比代码建模这一步我一般会把三个模型都放进去至少作为论文的对比实验。用三模型对比的另一个原因线性回归能给出正负系数随机森林能输出特征重要性XGBoost 能在复杂特征上取得较高精度三者各有用武之地。from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score X df[[rating, vote_count, duration, is_weekend, is_holiday, actor_heat, type_剧情, type_喜剧, type_科幻, type_动作]] y df[box_office_log] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) models { LinearRegression: LinearRegression(), RandomForest: RandomForestRegressor( n_estimators200, max_depth15, min_samples_leaf5, random_state42 ), } for name, model in models.items(): model.fit(X_train, y_train) pred model.predict(X_test) rmse np.sqrt(mean_squared_error(y_test, pred)) r2 r2_score(y_test, pred) mae mean_absolute_error(y_test, pred) print(f{name}: MAE{mae:.3f}, RMSE{rmse:.3f}, R2{r2:.3f})把box_office_log作为训练目标至少是一个正确的起点。如果不先做对数变换直接拿原始票房跑模型会被那些五十亿级的样本牵着走梯度方向完全取决于极少数大片。在box_office_log空间计算的 RMSE回到原始空间时要np.expm1还原论文中报告误差时记得用还原后的数字才贴近答辩老师的直觉。随机森林的重点参数max_depth比n_estimators敏感得多。n_estimators从 100 增加到 300训练时间线性增长但效果基本不动max_depth从 20 降到 10过拟合会明显减轻。min_samples_leaf5控制叶子最少样本数让树不会为了拟合单个极端样本而分裂到很细。这三个参数组合起来是随机森林在几千条小规模数据上最稳的起点。如果想往上走一步用 XGBoostimport xgboost as xgb xgb_model xgb.XGBRegressor( n_estimators500, learning_rate0.05, max_depth6, subsample0.8, colsample_bytree0.8, random_state42, ) xgb_model.fit(X_train, y_train) pred xgb_model.predict(X_test) print(fXGBoost: RMSE{np.sqrt(mean_squared_error(y_test, pred)):.3f}, fR2{r2_score(y_test, pred):.3f})XGBoost 的learning_rate要取 0.01 到 0.1 之间而不要用默认的 0.3。默认值在小数据集上收敛过快容易直接过拟合。subsample0.8是每棵树随机抽样 80% 的样本colsample_bytree0.8是每棵树随机选 80% 的特征这两个参数天然抗过拟合。先不要一股脑去调gamma、lambda那些高级参数在毕设项目里只会把网格搜索拖慢且收益极低把上面五个参数配好就已经比默认版本稳定很多。4.3 MAE、RMSE 与 R²指标怎么读模型的误差才敢写进论文答辩现场评委最常问的一句是你的模型预测误差是多少。误差必须量化否则论文难以服众。MAE 是平均绝对误差单位跟目标变量一致最容易向人解释。比如预测票房平均偏差 3500 万这句可以直接写进论文摘要。RMSE 是均方根误差因为平方的关系它会把大误差样本往往是 50 亿票房的片惩罚得更狠所以 RMSE 通常比 MAE 大。R² 是决定系数表示模型解释了多少方差但 R² 在预测类任务里容易虚高——如果数据里有几个超级大片模型只要能把它们蒙对R² 就很好看反之测试集里随机抽到几部冷门片R² 可能直接掉到 0.5 以下。我自己的习惯是三个指标同时报告因为单一指标都是片面的。一个合理的表格是模型名 / MAE / RMSE / R² / 训练时间在三行数据里一眼看出线性回归最差、随机森林中等、XGBoost 略好但差距不是数量级这是真实数据的常态。如果你的随机森林 R² 到了 0.98先别高兴回头检查特征是不是泄漏了。写论文时建议把 MAE 作为主要报告指标因为 RMSE 容易受极端值影响R² 又依赖数据分布。而 MAE 说平均偏差 3000 万是最直白的评委也最能理解。如果答辩时间有限就抓 MAE 和 R² 两个值MAE 说误差量级R² 说解释能力。5. 避坑指南这个项目最容易翻车的 5 个地方做这个项目绝大多数人不是死在模型上而是死在数据、环境和答辩细节上。下面 5 个坑都是比较典型的高频翻车点按自己踩过的频率排序。5.1 反爬返回 418为什么 requests 拿到的是验证码页现象明明设置了 User-Agent请求某电影网站时仍然返回 418 或 403解析出来是验证码页。原因网站会校验 Cookie 和浏览器行为没有 Cookie 的冷连接很容易被识别为爬虫放一个 4xx 状态码劝退。解决先用浏览器手动访问一次从开发者工具里复制 Cookie 字符串加到session.headers.update里。同时把请求间隔从固定 1 秒改成1~3 秒 随机数避免定时器式访问。如果仍然被拦截就改成调用网站官方提供的移动端 JSON 接口频率通常比 HTML 页面低得多。5.2 票房单位不统一预测结果差一个数量级的元凶现象爬回来的票房字段有时是3.5万有时是1.2亿直接float()转换报错。原因不同榜单的展示单位不同网页里拿到的原始文本也不是统一标准。有的榜单把 2.3 亿写成了2.3没有单位。解决写一个parse_box_office函数严格按亿/万处理。转换后再次验证print(df[box_office].describe())如果最大值超过 1000000100 亿以上那单位换算一定还有漏网之鱼。票房单位不统一是这类数据项目里最常见的翻车点尤其是跨多个数据源时每换一个网站都要重新检查一遍单位问题。5.3 中文字体缺失图上是方块答辩现场社死现象matplotlib 画出来所有中文都是口口口评委一看就知道你没处理环境问题。原因matplotlib 默认字体里不包含中文字形Windows 下往往需要手动指定 SimHei。解决设置plt.rcParams[font.sans-serif] [SimHei]同时设置axes.unicode_minus False。如果设置后中文仍是方块大概率是字体缓存问题删除C:/Users/用户名/.matplotlib缓存目录后重启内核。Linux 服务器上更直接apt install fonts-wqy-zenhei然后重新设置字体名。做好这一步可视化图表整体观感会立刻提升。5.4 日期解析失败混合格式让 pd.to_datetime 报 OutOfBoundsDatetime现象release_date列里既有2023年7月21日又有2023/07/21pd.to_datetime要么报错要么解析出一堆 NaT。原因pandas 默认解析器对混合格式支持有限而且遇到极端年份比如 1900 年以前的老片时有边界问题。解决指定format参数并把errorscoerce放在后面df[release_date] pd.to_datetime( df[release_date], format%Y年%m月%d日, errorscoerce )如果整列格式不统一format会水土不服这种情况下先按年提取再手动补月份和日df[release_year] df[release_date].str.extract(r(\d{4})).astype(float)解析失败的行会被errorscoerce转成 NaT接下来检查df[release_date].isna().sum()把无法解析的行挑出来人工看格式而不是放任 NaN 混进模型。5.5 特征泄漏与过拟合训练集 R² 0.98测试集 0.52现象训练集上 R² 达到 0.98测试集只有 0.52交叉验证时一验证就露馅。原因最典型的两个一是把想看人数、预售票房这类上映后才知道的信息当特征二是树模型完全不限深度纯粹把训练样本背了下来。解决建模前审视特征凡是包含上映后信息的列一律剔除用一句这部电影上映前能拿到的只有评分、类型、档期、主演来约束特征清单。对于随机森林设max_depth15、min_samples_leaf5并做 5 折交叉验证from sklearn.model_selection import cross_val_score model RandomForestRegressor(max_depth15, min_samples_leaf5, random_state42) scores cross_val_score(model, X, y, cv5, scoringr2) print(f5-fold R2: {scores.mean():.3f} ± {scores.std():.3f})交叉验证分数比单次train_test_split更可信。如果 5 折分数方差很大比如标准差超过 0.2说明数据里某几个样本影响极大这时考虑做分层采样或删除极端值而不是调更多模型参数。6. 让模型结果更可信SHAP 解释与项目文件组织6.1 用 SHAP 值回答为什么是这个特征答辩中常遇到你为什么觉得主演热度比类型更重要这类问题。用 SHAP 来解释模型是一步到位的做法import shap from sklearn.ensemble import RandomForestRegressor model RandomForestRegressor(max_depth15, min_samples_leaf5, random_state42) model.fit(X_train, y_train) explainer shap.TreeExplainer(model) shap_values explainer.shap_values(X_test) shap.summary_plot(shap_values, X_test, feature_namesX_test.columns)TreeExplainer是树模型专用线性回归要换成LinearExplainer。图上颜色越红表示该特征取值越大横轴正负代表对预测票房是推高还是压低。我自己的经验是rating 和 actor_heat 一般贡献最大is_holiday 在正方向上很明显——这就能直接写进论文上映档期对票房有正向贡献。6.2 项目目录怎么组织评审拿到 zip 后 3 分钟能跑起来最后一步是把项目文件整理得能复现。我一般习惯这样的目录结构ZIP 包解压后直接按 README 跑movies-analysis/ ├── README.md ├── requirements.txt ├── data/ │ ├── movie_raw.csv │ └── movie_clean.csv ├── code/ │ ├── 01_crawl.py │ ├── 02_clean.py │ ├── 03_visual.py │ └── 04_model.py ├── figures/ │ ├── correlation.png │ └── top10_bar.html └── doc/ ├── 论文.pdf └── 答辩.pptxrequirements.txt要把关键版本固定好比如pandas2.1.4、scikit-learn1.3.2、xgboost2.0.1。答辩现场如果装不了旧依赖最坏情况下在线安装最新版代码大概率也能跑但脚本会报弃用警告影响观感。我习惯在交付前把整个流程从一个干净的虚拟环境跑一遍不要只在 jupyter 里能跑。最后一遍验证实验时把测试集的预测值画成散点图横轴是实际票房纵轴是预测票房对角线代表完美预测——这张图只要落点基本贴在对角线附近整个项目基本就稳了。这个习惯是我从毕设踩坑里养出来的后来做工作也一直在用。希望帮到你。本文还有配套的精品资源点击获取
返回列表