news 2026/9/26 11:57:53

Python电影票房分析实战:从数据清洗到可视化全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python电影票房分析实战:从数据清洗到可视化全流程

简介:这份Python电影票房影响因素分析与可视化系统源码及文档,面向计算机及相关专业的学习者,适用于毕业设计、课程作业与项目实训等场景,帮助解决从数据处理到模型构建的全流程实践需求。资源包共46个文件,约6.03MB,以24张png可视化结果图、6个py核心脚本、3个ipynb分析笔记为主,另含sql建库脚本、pdf说明文档及md说明文件,覆盖数据采集、清洗、可视化与预测建模各环节。项目围绕豆瓣电影数据,通过多维可视化解析市场规律,并运用统计模型挖掘影响票房的关键变量,配套代码经多轮测试,可在主流Python环境下直接部署运行。目前已有27人学习,读者可借此掌握数据可视化技巧、影响因素相关性分析框架与预测模型构建流程,同时参考完整科研文档规范,为后续学术研究或职业发展积累标准化数据分析经验。

1. 从一张票房表到一套可复现的分析系统:这套 Python 方案到底解决什么问题

很多人第一次做电影票房分析,手里只有一份从公开渠道整理出来的 Excel:片名、上映日期、排片占比、首日票房、总票房、评分、类型、导演、主演,字段看着挺全,真跑起来却处处别扭。想算个相关系数,发现票房字段带「万」字;想画个趋势图,日期列是文本格式;想比较不同类型片的票房差异,类型字段里塞着「剧情/喜剧/爱情」这种复合值。于是大部分人的做法是手动改几行,画两张柱状图交差,结论停留在「喜剧片票房高」这种谁都知道的层面。

这套「Python 电影票房影响因素分析与可视化系统」要解决的,正是从原始表格到可解释结论之间的这段脏活。它不是一个预测模型比赛方案,而是一套完整的分析流水线:数据清洗、特征构造、影响因素量化、可视化呈现,最后落到一份能讲清楚「哪些因素和票房真正相关、相关强度多大、在什么条件下成立」的文档。适合两类人:一类是刚学完 pandas 和 matplotlib、想找一个完整项目练手的学生;另一类是需要快速搭一套分析看板、给业务方解释票房驱动因素的数据从业者。

我自己的经验是,票房分析最容易翻车的地方不在建模,而在数据本身。同一部电影在不同来源的票房数字能差出百分之十几,档期、排片、宣发这些关键变量又很难拿到干净数据。所以这套系统的价值不在于给出多精确的预测,而在于把「影响因素分析」这件事拆成可验证的步骤,每一步都能看到中间结果,出了问题能定位到具体环节。下面按数据准备、清洗、分析、可视化、避坑、进阶的顺序,把整套流程讲透。

2. 数据准备与字段设计:票房分析的第一道分水岭

2.1 先想清楚要分析哪些影响因素

在写任何代码之前,得先把「影响因素」这个词拆开。电影票房的影响因素大致分四类:影片自身属性(类型、时长、评分、导演和主演的号召力)、市场环境(档期、同期竞品数量、银幕数)、发行策略(排片占比、宣发投入、点映场次)、观众反馈(首周口碑、评分变化趋势)。这四类里,影片属性和观众反馈最容易拿到结构化数据,市场环境和发行策略往往需要额外采集,缺失也最严重。

我一般会先做一张字段清单表,把每个字段的来源、类型、缺失率、预期作用标清楚。这张表决定了后面清洗脚本要处理哪些异常,也决定了分析阶段能得出什么结论。如果排片数据缺失超过三成,那「排片对票房的影响」这个结论就不该写进文档,否则就是拿噪声当发现。

字段名类型来源预期缺失率分析用途
片名字符串公开票房榜低主键关联
上映日期日期公开票房榜低档期划分
总票房数值公开票房榜低目标变量
首日票房数值公开票房榜中首周爆发力
排片占比数值院线数据高发行策略
评分数值评分平台中口碑代理
类型字符串影片资料低分类维度
时长数值影片资料低影片属性
导演字符串影片资料低号召力代理
主演字符串影片资料中号召力代理

这张表不是摆设。后面每清洗一个字段,都要回来对照它的缺失率和用途,决定是填充、剔除还是保留但标注。很多分析报告结论站不住脚,就是因为没做这一步,拿一个缺失率百分之四十的字段算相关系数,还当成重要发现写进去。

2.2 用 pandas 读入并做第一轮体检

数据拿到手,第一步不是急着画图,而是做体检。下面这段代码读入原始表格,输出每列的类型、缺失率、唯一值数量和几个关键统计量。这一步的目的是让数据的问题自己暴露出来,而不是靠猜。

import pandas as pd import numpy as np # 读入原始数据,注意编码,中文表格常见 gbk 或 utf-8-sig df = pd.read_csv("movie_box_office.csv", encoding="utf-8-sig") # 第一轮体检:类型、缺失率、唯一值 def inspect(df): report = pd.DataFrame({ "dtype": df.dtypes, "missing_rate": df.isnull().mean().round(4), "n_unique": df.nunique(), "sample": [df[c].dropna().iloc[0] if df[c].notna().any() else None for c in df.columns] }) return report print(inspect(df)) # 数值列的描述统计,重点看 min/max 是否离谱 print(df.describe(include=[np.number]).T)

这段代码的关键在inspect函数:missing_rate告诉你哪些字段不能直接用,n_unique帮你发现本该是分类的字段被拆成了几百个唯一值(比如主演字段用顿号分隔),sample让你一眼看出票房字段是不是带单位。describe里的 min 和 max 尤其重要,如果总票房的 max 是 56 而 min 是 0.3,那基本可以确定单位不统一,有的按亿有的按万。

参数上,encoding要根据实际文件调整,中文 CSV 用utf-8-sig能避免表头出现乱码字符。如果数据是 Excel,换成pd.read_excel,但要注意openpyxl引擎对大文件较慢,超过十万行建议先转 CSV。

2.3 票房字段的单位统一与异常值处理

票房字段是重灾区。常见的有「12.5亿」「8500万」「1.2亿美元」混在一起,还有的直接是数字但单位不明。处理思路是先提取数值,再根据单位词换算成统一口径,我一般统一到「万元」。

import re def parse_box_office(value): """把带单位的票房字符串统一换算成万元""" if pd.isna(value): return np.nan value = str(value).strip() # 提取数字部分,支持小数 num = re.findall(r"[\d.]+", value) if not num: return np.nan num = float(num[0]) # 根据单位词换算 if "亿" in value: return num * 10000 if "万" in value: return num if "美元" in value: return num * 7.2 * 10000 / 10000 # 按汇率折算,单位仍为万元 return num # 无单位时按万元处理,需人工复核 df["总票房_万元"] = df["总票房"].apply(parse_box_office) df["首日票房_万元"] = df["首日票房"].apply(parse_box_office) # 异常值检查:票房为负或超过合理上限 print(df[df["总票房_万元"] < 0]) print(df[df["总票房_万元"] > 800000]) # 80亿以上人工复核

parse_box_office的逻辑是「先提数字,再看单位词」。这里有个坑:如果字符串里同时出现「亿」和「万」,比如「1.2亿3000万」,上面的写法只会取第一个数字,结果偏小。更稳妥的做法是分段匹配再相加,但实际数据里这种写法很少,我一般先按简单版跑,发现异常值再针对性处理。汇率折算那行只是示意,真实分析里如果混入美元票房,建议单独标注而不是直接换算,否则汇率波动会污染结论。

异常值检查不能只看代码跑通,要人工看几条。票房超过 80 万的(单位万元,即 80 亿)在国内市场极少,出现就要核对是不是单位重复换算。负值基本是数据录入错误,直接置为缺失。

3. 特征构造与影响因素量化:把「口碑」「档期」变成可计算的列

3.1 从日期字段拆出档期和上映星期

档期是票房分析里解释力很强的变量,但原始数据只有上映日期。需要从日期里拆出年份、月份、星期,再根据月份和节假日规则打上档期标签。春节档、暑期档、国庆档、贺岁档这几个是重点。

# 确保日期列是 datetime 类型 df["上映日期"] = pd.to_datetime(df["上映日期"], errors="coerce") df["上映年份"] = df["上映日期"].dt.year df["上映月份"] = df["上映日期"].dt.month df["上映星期"] = df["上映日期"].dt.dayofweek # 0 是周一 def label_season(month, day): """根据月份和日期打档期标签,规则可自行调整""" if month == 2 and 1 <= day <= 20: return "春节档" if month in [7, 8]: return "暑期档" if month == 10 and 1 <= day <= 7: return "国庆档" if month == 12 or (month == 1 and day <= 10): return "贺岁档" return "普通档" df["档期"] = df.apply(lambda r: label_season(r["上映月份"], r["上映日期"].day) if pd.notna(r["上映日期"]) else np.nan, axis=1)

label_season的规则是简化的,真实档期划分要看当年节假日安排,春节档的起止每年不同。我一般会把规则单独写成一个配置字典,方便按年份调整,而不是硬编码在函数里。dayofweek返回 0 到 6,周五到周日通常票房更高,这个字段在后续分组分析里很有用。

这里有个容易忽略的点:pd.to_datetime遇到格式不统一的日期会返回 NaT,errors="coerce"保证不报错但会丢数据。跑完要检查 NaT 的比例,如果超过百分之五,说明日期格式比预想的乱,需要先做格式归一化。

3.2 类型字段的拆分与多标签处理

电影类型经常是「剧情/喜剧/爱情」这种复合值,直接当分类变量用会导致类别过多。常见做法是拆成多个布尔列,每部电影可以同时属于多个类型。

# 按分隔符拆分类型,生成多标签布尔列 type_dummies = df["类型"].str.get_dummies(sep="/") type_dummies.columns = ["类型_" + c for c in type_dummies.columns] df = pd.concat([df, type_dummies], axis=1) # 统计各类型出现频次,低频类型考虑合并 type_counts = type_dummies.sum().sort_values(ascending=False) print(type_counts) # 出现少于 10 次的类型合并为「其他」 rare_types = type_counts[type_counts < 10].index df["类型_其他"] = df[rare_types].max(axis=1) df = df.drop(columns=rare_types)

str.get_dummies(sep="/")是 pandas 里处理分隔符多标签的常用写法,比手写循环快很多。拆完之后一定要看频次分布,出现次数个位数的类型在分组分析里没有统计意义,合并成「其他」更稳妥。合并时用max(axis=1)而不是sum,因为一部电影在同一类型上只会出现一次,max能正确表示「是否属于该大类」。

参数上,sep要根据实际分隔符调整,有的是「/」,有的是「、」或「,」。如果类型字段里有空格,比如「剧情 / 喜剧」,要先str.replace去掉空格再拆。

3.3 导演和主演号召力的量化思路

导演和主演是字符串,不能直接进模型。常见做法是用历史票房均值作为号召力代理:对每个导演,计算其历史作品的平均票房,作为该导演的号召力分数。主演同理,但主演字段往往是多人,需要先拆分。

# 计算导演历史平均票房(排除当前影片,避免数据泄漏) director_avg = df.groupby("导演")["总票房_万元"].mean().rename("导演号召力") df = df.merge(director_avg, left_on="导演", right_index=True, how="left") # 主演拆分:按分隔符拆成列表,再展开计算 def split_actors(s): if pd.isna(s): return [] return [a.strip() for a in re.split(r"[、/,]", s) if a.strip()] df["主演列表"] = df["主演"].apply(split_actors) # 展开后计算每个演员的平均票房 actor_rows = df.explode("主演列表") actor_avg = actor_rows.groupby("主演列表")["总票房_万元"].mean().rename("演员号召力") df = df.merge(actor_avg, left_on="主演", right_index=True, how="left")

这里有个数据泄漏的坑:用包含当前影片的全部数据算导演均值,会把目标变量信息带进特征。严格做法是留一法,即算某个导演均值时排除当前这部片。上面的代码为了简洁没做排除,实际分析里如果要用这个特征做预测,必须改成留一法,否则相关系数会虚高。

explode把一行多主演展开成多行,是处理列表型字段的标准操作。展开后 groupby 算均值再 merge 回来,注意 merge 的键要对应好,主演字段是原始字符串,不是列表,所以 merge 时用left_on="主演"而不是列表列,这里容易写错导致全为 NaN。

4. 影响因素分析与可视化:相关系数、分组对比和图表落地

4.1 相关系数矩阵与显著性判断

影响因素分析的核心是量化每个因素和目标变量的关系。数值型因素用皮尔逊相关系数,分类因素用分组均值对比或方差分析。先看数值型的。

from scipy import stats num_cols = ["总票房_万元", "首日票房_万元", "排片占比", "评分", "时长", "导演号召力"] corr_matrix = df[num_cols].corr(method="pearson") # 计算相关系数的 p 值 def corr_with_p(df, x, y): sub = df[[x, y]].dropna() r, p = stats.pearsonr(sub[x], sub[y]) return pd.Series({"r": round(r, 3), "p": round(p, 4), "n": len(sub)}) results = pd.DataFrame({c: corr_with_p(df, c, "总票房_万元") for c in num_cols if c != "总票房_万元"}).T print(results.sort_values("r", ascending=False))

corr_with_p同时输出相关系数、p 值和样本量,这三个缺一不可。只看 r 不看 p,可能把随机波动当成发现;只看 p 不看 n,小样本下的显著没有实际意义。我一般要求 n 大于 30 且 p 小于 0.05 才在文档里写成「显著相关」。

注意dropna是按对删除,不同变量对的样本量可能不同,这会导致相关系数之间不可直接比较。如果缺失严重,建议先做缺失值处理再统一计算,或者用df[num_cols].corr()的成对删除结果并标注样本量差异。

4.2 分组对比:不同类型和档期的票房差异

分类变量的分析用分组统计加可视化。下面按档期和类型分组,输出均值、中位数和样本量,再用箱线图呈现分布差异。

import matplotlib.pyplot as plt import seaborn as sns # 设置中文字体,避免乱码 plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False # 按档期分组统计 season_stats = df.groupby("档期")["总票房_万元"].agg(["mean", "median", "count"]).round(1) print(season_stats.sort_values("mean", ascending=False)) # 箱线图 fig, ax = plt.subplots(figsize=(10, 6)) sns.boxplot(data=df, x="档期", y="总票房_万元", ax=ax) ax.set_title("各档期票房分布对比") ax.set_ylabel("总票房(万元)") plt.xticks(rotation=30) plt.tight_layout() plt.savefig("season_boxplot.png", dpi=150)

groupby后同时看 mean 和 median 很重要。票房分布通常右偏,均值容易被少数爆款拉高,中位数更能反映典型水平。如果均值和中位数差距很大,说明分布偏斜严重,文档里要同时报告两个数,不能只写均值。

箱线图能直观看出分布差异和异常值。SimHei字体在 Windows 上通常可用,Linux 或 Mac 上可能需要换成Arial Unicode MS或指定字体文件路径。dpi=150保证导出图片清晰,用于文档足够。

4.3 用热力图和散点图呈现多因素关系

单变量分析之后,用热力图看整体相关结构,用散点图看关键变量的具体关系形态。

# 相关性热力图 fig, ax = plt.subplots(figsize=(8, 6)) sns.heatmap(corr_matrix, annot=True, fmt=".2f", cmap="RdBu_r", center=0, square=True, ax=ax) ax.set_title("数值变量相关性热力图") plt.tight_layout() plt.savefig("corr_heatmap.png", dpi=150) # 排片占比与票房的散点图,按档期着色 fig, ax = plt.subplots(figsize=(10, 6)) sns.scatterplot(data=df, x="排片占比", y="总票房_万元", hue="档期", alpha=0.7, ax=ax) ax.set_title("排片占比与总票房关系(按档期着色)") plt.tight_layout() plt.savefig("scatter_screen.png", dpi=150)

热力图的center=0让颜色以零为中心对称,正相关偏红负相关偏蓝,比默认配色更容易读。annot=True显示数值,但变量多时会拥挤,超过八个变量建议关掉标注只保留颜色。

散点图按档期着色能看出分层效应:如果春节档的点整体偏右上,说明档期和排片存在交互作用,单看排片相关系数会低估档期的贡献。这种图在文档里比一张干巴巴的相关系数表有说服力得多。

5. 避坑与排查:票房分析里最容易翻车的五个地方

5.1 票房单位不统一导致相关系数完全失真

现象:算出来排片占比和票房的相关系数是 0.12,明显偏低,但业务上排片和票房应该强相关。

原因:票房字段里混着「亿」和「万」,清洗时只提取了数字没换算单位,导致部分影片票房被缩小一万倍,整体分布被拉平。

解决:在parse_box_office里强制单位归一,跑完后用describe检查 min 和 max 是否在合理区间,再抽样人工核对十条记录。单位处理完之前不要做任何分析。

5.2 用全量数据算导演号召力造成数据泄漏

现象:导演号召力和票房的相关系数高达 0.85,看起来是个强特征,但换成新数据预测时效果一塌糊涂。

原因:计算导演均值时包含了当前影片自身的票房,等于把答案提前告诉了特征。

解决:改用留一法,算某个导演的号召力时排除当前这部片。样本量小的导演(作品少于三部)直接置为缺失,不要用单部作品均值代替。

5.3 日期解析失败导致档期标签大面积缺失

现象:档期分组统计里「普通档」占了八成,春节档和国庆档样本极少,和常识不符。

原因:pd.to_datetime遇到「2023.1.15」这种点分隔格式返回 NaT,档期标签函数收到 NaT 后走了默认分支。

解决:解析前先用str.replace把点、斜杠统一成横杠,或者给to_datetime传format参数指定格式。解析后检查 NaT 比例,超过百分之五就先修格式再往下走。

5.4 中文字体缺失导致图表全是方框

现象:本地跑图正常,换一台机器或导出到文档里中文全变成方框。

原因:SimHei字体在目标环境不存在,matplotlib 回退到默认字体,中文无法渲染。

解决:不要硬编码字体名,先检查可用字体列表,或者直接把字体文件路径传给font_manager。跨平台项目建议把字体文件放进项目目录,用相对路径加载,保证到哪都能跑。

5.5 缺失值处理方式不一致导致结论矛盾

现象:同一份数据,两次分析得出排片和票房一个强相关一个弱相关。

原因:一次用了dropna按对删除,一次用了均值填充,两种方式对缺失样本的处理不同,样本集变了结论自然变。

解决:在分析开始前统一缺失值策略,写进文档。数值型字段缺失超过三成直接剔除,低于三成用中位数填充并加缺失指示列。所有分析基于同一份处理后的数据,不要中途换策略。

6. 进阶技巧:把分析结果做成可复用的报告模板

前面五章把流程跑通了,但每次换一批数据都要重跑脚本、重调图表,效率很低。我一般会把整套逻辑封装成一个配置驱动的分析模板,数据路径、字段映射、图表开关都写在配置文件里,换数据只改配置不改代码。

import yaml # config.yaml 示例 # data_path: data/movie_2024.csv # target_col: 总票房 # num_features: [排片占比, 评分, 时长] # cat_features: [档期, 类型] # output_dir: reports/2024 def load_config(path): with open(path, "r", encoding="utf-8") as f: return yaml.safe_load(f) def run_analysis(config): df = pd.read_csv(config["data_path"], encoding="utf-8-sig") df["总票房_万元"] = df[config["target_col"]].apply(parse_box_office) # 后续清洗、分析、绘图按配置里的字段列表执行 # 输出报告到 config["output_dir"] return df config = load_config("config.yaml") df = run_analysis(config)

配置驱动的核心价值是可复现。同一份配置跑出来的结果,换个人、换台机器应该一致。我习惯在输出目录里同时存一份配置副本和运行时间戳,方便回溯。图表开关用布尔值控制,探索阶段全开,出报告时只留关键几张,避免文档里堆满图。

验证分析结果是否可靠,我常用一个笨办法:把数据随机分成两半,分别跑一遍相关系数和分组统计,看结论是否一致。如果两半数据得出的排片相关系数差了 0.2 以上,说明样本量不够或数据噪声太大,结论要谨慎写。这个方法比任何统计检验都直观,也最容易发现隐藏的数据问题。

最后说个习惯:每次分析完,我会把「哪些结论站得住、哪些只是相关不是因果、哪些字段缺失太严重没纳入」单独写一段放进文档。票房分析里因果推断极难,排片高可能因为片方看好,票房好也可能反过来推高排片,这种互为因果的关系在文档里必须说清楚,否则读者容易把相关当因果用。这套系统能帮你把数据理干净、把关系量化出来,但结论的边界得自己守住。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 11:57:51

GitHub Copilot 为何在部分开发场景中成为鸡肋

1. 被神化的补全工具&#xff0c;为什么在我们手里成了鸡肋第一次听说 GitHub Copilot 是在一个技术群里&#xff0c;有人发了一张截图&#xff0c;说写代码的时候它能把整段逻辑补全&#xff0c;连注释都帮你写好了。群里一片惊叹&#xff0c;仿佛程序员的饭碗明天就要被端走。…

作者头像 李华
网站建设 2026/9/26 11:55:13

Docker容器名与服务名解析:内建DNS底层逻辑与排障实践

先说一个多数人踩过的坑&#xff1a;把容器跑起来之后&#xff0c;在另一个容器里直接用容器名去 ping &#xff0c;结果发现经常不通&#xff1b;但用 IP 地址就能通。一旦换成 docker compose 编排&#xff0c;服务名却又能通了。很多人第一反应是“网络模式不同”&#…

作者头像 李华
网站建设 2026/9/26 11:54:33

飞牛fnOS部署HomeBox:用Docker打造家庭资产管理系统

家里东西多了以后&#xff0c;总会遇到一个让人抓狂的瞬间&#xff1a;明明刚买回来的设备&#xff0c;过两个月就忘了塞在哪个箱子。与其每次靠记忆翻箱倒柜&#xff0c;不如把每一件资产的位置、照片、价格、保修信息都收进一个数据库。最近我在飞牛&#xff08;fnOS&#xf…

作者头像 李华
网站建设 2026/9/26 11:54:00

PHP积分商城系统源码解析:从积分链路到兑换码核销的二次开发实践

简介&#xff1a;这是一套面向PHP开发者的开源积分商城系统源码&#xff0c;适用于搭建积分兑换、商品展示与兑换码发放的电商平台。系统支持PC与WAP端访问&#xff0c;内置一键生成唯一兑换码功能&#xff0c;可有效防止重复兑换&#xff1b;源码开放&#xff0c;便于二次开发…

作者头像 李华
网站建设 2026/9/26 11:53:59

STM32嵌入式AI模型设计:从Model Zoo到自有模型的完整实操指南

1. 先搞清楚 ST 的 Model Zoo 到底装了什么ST 官方这几年在嵌入式 AI 这条线上铺得很快&#xff0c;从 STM32Cube.AI 到 NanoEdge AI Studio&#xff0c;再到 X-CUBE-AI 扩展包&#xff0c;整个工具链已经能覆盖从模型转换、量化、部署到性能评估的完整流程。Model Zoo 就是这套…

作者头像 李华