
简介这是一份面向计算机相关专业毕业设计及项目实战学习者的电影数据分析完整源码包围绕“电影数据可视化与票房影响因素分析与预测”展开涵盖数据采集、数据库存储、可视化呈现和票房预测等核心环节。资源共38个文件包括6个Python功能脚本、3个Jupyter Notebook交互式分析文档、1个SQL数据库文件以及PDF说明文档和大量结果图表压缩包仅5.18MB便于下载与部署。已有424人学习使用。项目代码经过严格调试下载后可直接运行适合作为毕业设计、课程设计或期末大作业配套的可视化截图和说明文档能帮助快速理解票房影响因素的分析思路也能为后续功能扩展提供清晰的项目结构参考。1. 一个票房分析毕设项目的正确打开方式拿到这个压缩包先别急着双击 README.pdf。先把文件结构对应起来main.py 是程序入口database.py 负责把 douban.sql 导入 SQLite 并建立连接movie_basic.py 和 movie_detail.py 分别处理基础信息与票房明细predict.ipynb 做票房影响因素分析与预测visualization_pandas.ipynb 和 visualization_sql.ipynb 是两条可视化路线。result 目录里的 p(1).png 到 p(19).png 是过程图预测1.png 和预测2.png 是模型输出。这类基于 python 的电影数据可视化及票房影响因素分析项目在毕业设计和课程设计里很常见因为它链条完整数据入库、多表关联、统计图表、回归模型一个不落所以很适合拿来当全套实践模板。适合手头已经有一份电影数据集、想直接跑通整套分析流程的人也适合需要快速搭建毕设代码框架的计算机专业学生。下面按实际执行顺序把关键代码、参数设置和容易翻车的地方过一遍。2. 先把数据落库douban.sql 与 database.py 的初始化逻辑2.1 从表结构反推项目的数据边界douban.sql 是整套分析的源头。建议先用 sqlite3 命令行打开看一眼表结构确认字段命名规则再动手跑代码sqlite3 douban.db .tables .schema movie_basic从文件命名和常见豆瓣数据项目约定来看作者不是把所有字段塞进一张宽表而是拆成基础表和明细表这样的 join 语义更清晰表名用途关键字段movie_basic电影基础信息id, title, director, actors, genre, release_date, durationmovie_detail票房与口碑明细movie_id, box_office, rating, votes, show_count, avg_pricedouban_comment评论摘要movie_id, comment_count, positive_rate, repost_countmovie_basic 存身份信息movie_detail 通过 movie_id 关联到具体影片存的是票房、评分、评价人数这类表现数据。douban_comment 则用来分析口碑对票房的影响属于扩展表可以后续接入分词和情感分析。这个拆法的好处是改导演、演员这类基础信息不会影响票房表后续如果要补新字段比如补一条 film_location只需要在明细表加列。坏处是关联字段类型一旦不一致就会查不出数据。movie_id 在 basic 表是 int在 detail 表被存成 textSQLite 做隐式连接时可能出结果错乱所以第一次跑通项目后建议加一个断言import pandas as pd import sqlite3 conn sqlite3.connect(douban.db) df_basic pd.read_sql(SELECT id FROM movie_basic, conn) df_detail pd.read_sql(SELECT movie_id FROM movie_detail, conn) assert df_basic[id].dtype df_detail[movie_id].dtype, 关联字段类型不一致这个检查点很多毕设项目不会主动做但它能避免后续 join 结果里出现 NULL 或者条目数对不上的情况。如果你看到的字段名不是上面这套以 README.pdf 里实际的建表语句为准分析思路不变。2.2 database.py 的建表逻辑src 下的 database.py 承担了初始化职责常见写法如下import sqlite3 import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) DB_PATH os.path.join(BASE_DIR, douban.db) def init_db(sql_filedouban.sql): conn sqlite3.connect(DB_PATH) with open(sql_file, r, encodingutf-8) as f: conn.executescript(f.read()) conn.commit() conn.close() def get_conn(): return sqlite3.connect(DB_PATH) if __name__ __main__: init_db()BASE_DIR 用 os.path.abspath 而不是直接写相对路径这一点能让其他模块从任何工作目录启动时都找到 douban.db。DB_PATH 设成全局变量visualization_pandas.ipynb 和 predict.ipynb 都可以直接 import database 模块来复用它不用各自维护一份连接字符串。执行建表用的是 executescript 而不是 execute因为 douban.sql 里通常是多条 CREATE TABLE 语句executescript 能一次执行完还支持执行完自动提交。需要留意的是sql 文件里不能带参数占位符否则 executescript 会解析失败。这个函数跑完之后项目才能进入数据读取环节。2.3 attachfile.py 的定位与数据导入兜底attachfile.py 从名字看是附加文件用途是把额外数据挂到已有库上。很多毕设的数据不是一次到位先给 douban.sql后来又补一个 movies.csv这时 attachfile.py 就是兜底导入脚本。常见做法是用 pandas 读出外部文件清洗后再 to_sql 追加import pandas as pd import sqlite3 from database import DB_PATH, init_db init_db() df pd.read_csv(movies.csv, encodingutf-8, thousands,) df[box_office] pd.to_numeric(df[box_office], errorscoerce) conn sqlite3.connect(DB_PATH) df.to_sql(movie_detail, conn, if_existsappend, indexFalse) conn.close()这段代码有三个参数值得说明。thousands, 处理 CSV 里带千分位的数字比如 12,345 能正确转成 12345否则 pandas 会把它当成字符串。pd.to_numeric 配合 errorscoerce 把无法解析的脏数据置为 NaN保证入库不会中断。if_existsappend 表示追加而不是 replace避免覆盖 database.py 建好的表结构。还有一个容易踩的编码坑如果 CSV 是从 Excel 另存出来的多半是 GBK读取时要改成 encodinggbk否则第一列会变成乱码字段名。豆瓣系数据导出一般带 UTF-8 BOMpandas 默认能识别Windows 记事本转存过之后反而可能引入 BOM读出来后列名会带 \ufeff 前缀用 df.columns df.columns.str.replace(\ufeff, ) 清一下即可。3. 可视化复现pandas 直连与 SQL 聚合两条路线3.1 为什么要保留两套可视化 notebookvisualization_pandas.ipynb 和 visualization_sql.ipynb 处理的是同一份数据库但写作思路完全不同。pandas 路线强调探索式分析先把多张表一次性读进内存然后反复 groupby、merge、透视改图参数非常快。SQL 路线强调口径固化票房按年聚合、评价人数按类型聚合这类统计全部写成 SQL数据库算完再交给 matplotlib数据量上去后聚合速度更快也方便把同一段 SQL 复制到其他 BI 工具里核对结果。两条路线可以这样选场景推荐路线原因快速预览字段分布pandas直接 .describe() 和 .hist() 一行出图验证聚合统计口径SQL聚合逻辑看得见结果可复核数据量超过 20 万行SQL提前聚合减少网络传输和内存压力图表参数反复调优pandas内存中计算实时响应更快初学阶段先照着 visualization_pandas.ipynb 改把图形参数调明白答辩前再用 visualization_sql.ipynb 里的 SQL 做交叉验证。两条路径算出同一张图说明数据口径没问题这比单独画出一张好看图更有说服力。3.2 pandas 直连路线先看年度票房走势visualization_pandas.ipynb 里最基础的一张图是年度票房走势实际代码可以精简成这样import sqlite3 import pandas as pd import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False conn sqlite3.connect(douban.db) df pd.read_sql( SELECT b.release_date, d.box_office, d.rating, d.votes FROM movie_basic b JOIN movie_detail d ON b.id d.movie_id , conn) df[year] pd.to_datetime(df[release_date]).dt.year yearly df.groupby(year)[box_office].sum() / 10000 yearly.plot(kindbar, figsize(12, 5)) plt.title(年度总票房走势万元) plt.savefig(result/output.png, dpi150)这里最关键的是 pd.to_datetime 先把 release_date 字符串统一成 datetime 类型再取 dt.year。如果直接用字符串 groupby会得到每年一张表但排序错乱的折线图。若原始数据里日期格式混用比如既有 2024-05-01 又有 2024年5月1日就要加 format 参数否则 pandas 会抛解析异常。figsize(12, 5) 控制出图尺寸dpi150 是给 result 目录输出 PNG 用的屏幕预览调到 80 就够。result 目录里那一堆 p(1).png 到 p(19).png 其实是 notebook 各 cell 保存的中间图用于记录过程不用纠结命名顺序。真正有验收意义的是 output.png、db.png、db_struct.png 和预测1.png、预测2.pngdb.png 是数据库连接结果截图db_struct.png 是表结构图output.png 是最终可视化大图。3.3 SQL 聚合路线把统计下推给 SQLitepandas 路线的问题在于所有数据都先拉进内存。当 movie_detail 扩展到几十万行评论数据后内存占用和画图响应都会有明显卡顿。SQL 路线先做聚合传输和内存压力都小得多。visualization_sql.ipynb 里的核心 SQL 大概是这个形状SELECT strftime(%Y, b.release_date) AS year, SUM(d.box_office) AS total_box, AVG(d.rating) AS avg_rating, COUNT(*) AS movie_cnt FROM movie_basic b JOIN movie_detail d ON b.id d.movie_id WHERE b.release_date ! GROUP BY year ORDER BY year;strftime(%Y, ...) 是 SQLite 的日期格式化函数等价于 pandas 的 dt.year。这里不要直接 GROUP BY release_date否则同一年的日期会被拆成很多行。WHERE release_date ! 用来过滤空串脏数据这个条件在 pandas 路线里往往被忽略结果就是年度总票房少算一部分。COUNT(*) 统计影片数可以辅助判断样本量如果某年只有一两部电影平均评分高并不代表市场好看图时要结合电影数量一起判断。读回这段 SQL 结果后再画图可以考虑双 y 轴yearly pd.read_sql(QUERY, conn) fig, ax1 plt.subplots(figsize(12, 6)) ax1.bar(yearly[year], yearly[total_box] / 10000, color#4C72B0) ax1.set_ylabel(票房万元) ax2 ax1.twinx() ax2.plot(yearly[year], yearly[avg_rating], color#C44E52, markero) ax2.set_ylabel(平均评分)twinx() 是双 y 轴画法左边柱子看票房右边折线看评分这两组量纲差距太大不能直接叠在同一条轴上。颜色选的是四色色盲友好的 #4C72B0 和 #C44E52答辩投屏时比 matplotlib 默认配色的可读性好很多。3.4 unit.py 的用途把票房单位对齐unit.py 在 src 目录里容易被忽略它的价值在于单位换算。电影票房字段可能来自不同来源有的是元有的是万元有的是亿元。如果混着用bar 图里会出现一个异常高的孤立柱子。unit.py 里常见做法是提供 to_yi 和 to_wan 两个函数def to_yi(value, raw_unitwan): if raw_unit wan: return value / 10000.0 if raw_unit yuan: return value / 100000000.0 return value def to_wan(value): return value / 10000.0统一单位这一步不能省。我处理类似数据时吃过亏某平台导出票房单位是万元另一张表是原始元合并后总票房被放大了 10000 倍画出来的图直接穿出坐标系。在进可视化之前先对票房列做一次 describe看 max 和 min 的比值是否在合理范围内远比事后查图更高效。4. 票房影响因素分析与随机森林预测4.1 从可视化到建模哪些特征能进模型分析做到这步predict.ipynb 要回答的问题是已知一部电影上映前的可观测信息预估它的最终票房。注意上映前这三个字很关键。评分和评价人数虽然是强特征但在上映前拿不到所以严格意义上的预测模型应该只用档期、导演、演员、类型这类事前特征。实操中很多论文把评分也放进去得到的 R2 虚高答辩时被问一句你这个模型能提前多久预测很容易答不上来。predict.ipynb 里通常这样构造特征特征名原始来源加工方式ratingmovie_detail直接取值缺失填 0votesmovie_detaillog1p 压缩长尾durationmovie_basic直接取值缺失填中位数release_monthmovie_basic由 release_date 拆分is_holiday外部标记春节、国庆档记为 1director_level导演历史票房均值按导演聚合后回填最容易翻车的仍然是 release_date直接把字符串丢给模型会报 ValueError必须先转换成数值。release_month 是 1 到 12 的整数喂给树模型没问题但如果换用线性回归最好做 one-hot 或者 sin/cos 编码否则模型会学出12 月大于 11 月的错误单调关系。4.2 训练代码随机森林比线性回归适合票房数据票房和特征之间不是线性关系档期在决策树里天然是分段函数评分越高边际效应越小到 8.5 分以后对票房的拉动明显放缓。所以我一般不用 LinearRegression 打底而是直接上随机森林特征重要性输出还能撑起一段分析论述from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import r2_score, mean_squared_error features [rating, votes, duration, release_month, is_holiday, director_level] X df[features].fillna(0) y df[box_office] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model RandomForestRegressor(n_estimators300, max_depth10, n_jobs-1, random_state42) model.fit(X_train, y_train) y_pred model.predict(X_test) print(R2:, round(r2_score(y_test, y_pred), 4)) print(RMSE:, round(mean_squared_error(y_test, y_pred, squaredFalse), 2)) print(feature_importances:, dict(zip(features, model.feature_importances_)))test_size0.2 表示 20% 样本不参与训练random_state42 让每次切分结果一致保证答辩时能复现同一条误差曲线。n_estimators300 是树的数量太高了训练慢且收益递减max_depth10 限制树的深度防止树长到过拟合n_jobs-1 用满 CPU 核数这个数据量几秒就能跑完。fillna(0) 是粗暴做法只适合0 表示没有的场景。如果不确定缺失值语义建议用中位数填充或者直接删除缺失行避免给模型引入评分 0 分这种离群样本。R2 离 1 越近解释力越强RMSE 的单位就是票房单位可以直接说明平均误差是多少万元。4.3 预测结果图与提升路径预测1.png 和预测2.png 是 predict.ipynb 保存的两张输出。预测1.png 通常是真实票房和预测票房的散点图点越靠近对角线说明预测越准预测2.png 是特征重要性条形图rating 和 votes 的 importance 通常会排在最前面。看到这个结果别急着高兴这恰恰说明模型捕捉的是上映后评价而不是上映前可预测性。如果想在答辩时体现思考深度可以把 rating 和 votes 从特征中删掉模型会明显变差但此时的预测才真正有业务价值。另一个优化是处理票房长尾分布import numpy as np model.fit(X_train, np.log1p(y_train)) y_pred np.expm1(model.predict(X_test)) print(R2:, round(r2_score(y_test, y_pred), 4))log1p 把右偏的票房分布拉正避免模型把大量拟合能力浪费在高票房的少数大片上预测完再用 expm1 还原得到的 RMSE 依然是原始票房单位。这个改动通常会把 R2 提升几个百分点可以单独作为一节写在论文的实验分析里。5. 下载即用环境、排错与二次开发入口5.1 先锁环境再跑 main.py压缩包里的 README.pdf 会列依赖但不少毕设 README 是模板生成环境写得不全。建议先建虚拟环境避免污染系统 Pythonpython -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate pip install pandas scikit-learn matplotlib jupyter版本上按这个范围选较稳妥依赖建议版本python3.9 ~ 3.11pandas1.5.x ~ 2.xscikit-learn1.2 及以上matplotlib3.6 及以上如果 Jupyter 一直报找不到 pandas大概率是内核选的不是这个虚拟环境的 Python打开 notebook 后把 Kernel 切到 venv 即可。5.2 三个常见报错第一sqlite3.OperationalError: no such table: movie_detail。原因是没先执行 database.py或者 DB_PATH 和 notebook 工作目录不一致。解决方式是在 notebook 第一格先跑一遍 init_db()再用绝对路径连接。第二matplotlib 中文乱码。项目里用的是 SimHei代码里要设置plt.rcParams[font.sans-serif] [SimHei]和plt.rcParams[axes.unicode_minus] False。Linux 服务器上没有这个字体需要装 fonts-wqy-zenhei。第三pandas 2.x 里 pd.read_sql 直接传 sqlite3.Connection 虽然能用但会报未来弃用警告。稳定的写法是from sqlalchemy import create_engine engine create_engine(sqlite:///douban.db) df pd.read_sql(SELECT * FROM movie_detail, engine)提示每次改完 douban.sql 后如果新加表没生效先删掉旧的 douban.db 再重新执行 init_db()SQLite 不会自动同步表结构。5.3 二次开发入口main.py 是汇总入口跑通后想换数据源最直接的方式是替换 database.py 里的 DB_PATH或者把 douban.sql 换成自己爬虫清洗后的新表。只要表名和字段名与 predict.ipynb 里引用的一致改动量能控制在 20 行以内。一个值得试的方向把 is_holiday 从 0/1 扩展成春节、暑假、国庆、其他四分类重新训练后对比特征重要性排名看哪类档期对票房的解释力最强。把 main.py 里的月份参数改成 12再跑一遍 visualization_sql.ipynb 中的档期聚合曲线圣诞节档期对票房的拉动通常比国庆更陡这就是改参数、看图像、出结论的完整闭环。本文还有配套的精品资源点击获取