简介:这份资源面向计算机、人工智能、大数据、数学及电子信息等相关专业的学生与技术学习者,提供一套基于Python的电影数据集分析实践项目,可用于课程设计、期末大作业或毕业设计的参考。压缩包共3个文件,包含1个py脚本、1个ipynb笔记本和1个html页面,整体约674KB,分别对应可执行分析代码、交互式探索过程与结果展示,便于对照理解数据清洗、统计与可视化的完整流程。目前已有134人学习下载。项目代码经过调试,下载后可直接运行,读者能借此掌握用Python处理真实电影数据的思路,包括字段解析、分组聚合、趋势观察与图表呈现等环节,并参考其代码组织方式与排错逻辑,快速搭建自己的分析框架,适合具备一定Python基础、希望积累数据分析实战经验的学习者使用。
1. 电影数据集分析包:从 Notebook 到可复现脚本的完整落地
很多同学拿到「电影数据集分析」这类资源,第一反应是打开 Notebook 从头跑到尾,看到图表出来就以为完事了。但真正做课程设计或毕设答辩时,老师问一句「你这个评分分布是怎么算的、缺失值怎么处理的」,往往就卡住了。这个资源包的核心价值不在于图表多好看,而在于它提供了一条从原始 CSV 到可视化结论的完整链路:Explore Movie Dataset.ipynb负责交互式探索,Explore Movie Dataset.py是等价的脚本版本,Explore Movie Dataset.html则是导出后的静态报告,方便直接放进论文附录或答辩材料。它适合正在做数据分析课程设计、期末大作业的学生,也适合想拿一个真实数据集练手 pandas 和 matplotlib 的入门者。前提是你得愿意读懂每一行代码在干什么,而不是只按 Shift+Enter。
2. 环境准备与数据加载:把依赖和数据路径先理顺
2.1 依赖清单与安装方式
这个项目用到的库都是数据分析的常规组合,不需要额外配置复杂环境。常见做法是建一个独立虚拟环境,避免和系统 Python 里的包版本打架。我一般会先确认 Python 版本,再一次性装齐。
# 创建虚拟环境(推荐,避免污染全局环境) python -m venv movie_env # 激活虚拟环境 # Windows: movie_env\Scripts\activate # macOS / Linux: source movie_env/bin/activate # 安装核心依赖 pip install pandas numpy matplotlib seaborn jupyter这里几个包的分工要说清楚:pandas负责数据读取和清洗,numpy处理数值计算,matplotlib和seaborn负责画图,jupyter用来跑.ipynb文件。版本上没有强制要求,但建议 pandas 不低于 1.3,否则某些groupby的写法会报 warning。如果你用的是 Anaconda,这些库基本都自带了,直接跳过安装步骤即可。
提示:如果
pip install速度慢,可以换国内镜像源,加上-i https://pypi.tuna.tsinghua.edu.cn/simple参数。
2.2 数据加载与首次体检
拿到数据集后,不要急着画图。先做三件事:看形状、看类型、看缺失。这三步能帮你避开后面 80% 的翻车。
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 读取数据,注意编码问题,常见的有 utf-8 和 gbk df = pd.read_csv('movies.csv', encoding='utf-8') # 第一步:看数据规模和列名 print('数据形状:', df.shape) print('列名列表:', df.columns.tolist()) # 第二步:看每列的数据类型和非空数量 print(df.info()) # 第三步:看缺失值分布 missing = df.isnull().sum() missing_pct = (missing / len(df) * 100).round(2) missing_df = pd.DataFrame({'缺失数': missing, '缺失占比%': missing_pct}) print(missing_df[missing_df['缺失数'] > 0].sort_values('缺失数', ascending=False))这段代码的逻辑很直白:shape告诉你有多少行多少列,info()让你看清哪些列是数值型、哪些是字符串型,缺失值统计则决定你后面要不要做填充或删除。参数上唯一需要注意的是encoding,如果报UnicodeDecodeError,换成gbk或latin-1再试。很多新手在这一步就卡住,以为是代码问题,其实只是编码不对。
2.3 字段含义与选型判断
电影数据集通常包含这些字段:预算(budget)、票房(revenue)、评分(vote_average)、投票数(vote_count)、类型(genres)、上映日期(release_date)、片长(runtime)等。不同来源的数据集列名可能略有差异,但核心字段大同小异。你需要先确认哪些字段适合做数值分析,哪些适合做分类统计。
比如genres字段往往是一串用竖线或逗号分隔的标签,直接拿来 groupby 没有意义,得先拆分。release_date是字符串,要转成 datetime 才能按年份聚合。这些转换在 Notebook 里都有对应代码,但你要理解为什么这么做,而不是照抄。
3. 数据清洗与特征构造:决定分析质量的关键一步
3.1 缺失值处理策略
电影数据里最常见的缺失集中在预算、票房和片长上。处理方式取决于你的分析目标:如果只是看评分分布,预算缺失可以不管;但如果要做「预算与票房的关系」,那就必须处理。
# 查看关键字段的缺失情况 key_cols = ['budget', 'revenue', 'runtime', 'vote_average'] print(df[key_cols].describe()) # 策略一:删除关键字段缺失的行(适合缺失比例低的情况) df_clean = df.dropna(subset=['budget', 'revenue']) # 策略二:用中位数填充(适合缺失比例较高但不想丢数据) df['runtime'] = df['runtime'].fillna(df['runtime'].median()) # 策略三:标记缺失(适合缺失本身有意义的场景) df['budget_missing'] = df['budget'].isnull().astype(int)三种策略没有绝对优劣。dropna简单粗暴,但如果缺失比例超过 20%,删完可能就没剩多少数据了。中位数填充保守但会引入偏差。标记缺失则适合做后续分析时把「是否缺失」本身作为一个特征。我一般会先看缺失比例,低于 5% 就删,高于 20% 就考虑标记,中间地带用中位数。
3.2 类型字段拆分与票房归一化
genres字段是典型的「一列多值」,需要拆成多行或者做 one-hot 编码。同时,预算和票房往往量级很大,直接画散点图会挤在一起,需要做对数变换。
# 拆分 genres 字段:把 "Action|Adventure|Sci-Fi" 拆成单独的行 df_exploded = df_clean.copy() df_exploded['genres'] = df_exploded['genres'].str.split('|') df_exploded = df_exploded.explode('genres') # 统计每个类型的电影数量 genre_counts = df_exploded['genres'].value_counts() print(genre_counts.head(10)) # 对预算和票房做对数变换,避免极端值影响可视化 df_clean['log_budget'] = np.log1p(df_clean['budget']) df_clean['log_revenue'] = np.log1p(df_clean['revenue']) # 计算ROI(投资回报率),注意除零问题 df_clean['roi'] = df_clean.apply( lambda row: row['revenue'] / row['budget'] if row['budget'] > 0 else np.nan, axis=1 )explode是 pandas 0.25 之后才有的方法,作用是把列表型的单元格展开成多行。np.log1p等价于log(1+x),好处是当 x 为 0 时不会报错。ROI 计算里加了budget > 0的判断,因为数据里确实存在预算为 0 的记录,直接除会得到 inf。这些细节在 Notebook 里可能一笔带过,但你自己写的时候不注意就会踩坑。
3.3 时间字段解析与年份特征
release_date通常是字符串格式,需要转成 datetime 才能提取年份和月份。
# 转换日期格式,errors='coerce' 会把无法解析的值变成 NaT df_clean['release_date'] = pd.to_datetime(df_clean['release_date'], errors='coerce') # 提取年份和月份 df_clean['release_year'] = df_clean['release_date'].dt.year df_clean['release_month'] = df_clean['release_date'].dt.month # 按年份统计电影数量 yearly_count = df_clean.groupby('release_year').size() print(yearly_count.tail(20))errors='coerce'这个参数很关键。如果不用它,遇到格式不对的日期会直接抛异常;用了之后,无法解析的值变成NaT,后续可以用dropna处理。年份提取后可以做趋势分析,月份提取后可以看上映档期的分布规律,这些都是课程设计里容易出彩的点。
4. 可视化与结论输出:让图表真正说明问题
4.1 评分分布与票房关系图
可视化的核心原则是:一张图只说一件事。不要试图在一张图里塞进所有信息。
# 设置中文字体,避免中文显示为方块 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 图1:评分分布直方图 fig, axes = plt.subplots(1, 2, figsize=(14, 5)) axes[0].hist(df_clean['vote_average'].dropna(), bins=30, color='steelblue', edgecolor='white') axes[0].set_title('电影评分分布') axes[0].set_xlabel('评分') axes[0].set_ylabel('电影数量') # 图2:预算与票房散点图(对数坐标) axes[1].scatter(df_clean['log_budget'], df_clean['log_revenue'], alpha=0.3, s=10, color='coral') axes[1].set_title('预算与票房关系(对数坐标)') axes[1].set_xlabel('预算(对数)') axes[1].set_ylabel('票房(对数)') plt.tight_layout() plt.savefig('rating_and_revenue.png', dpi=150) plt.show()alpha=0.3让散点半透明,避免重叠区域看不清密度。s=10控制点的大小。dpi=150保证保存的图片足够清晰,放进论文不会糊。中文字体设置是必须的,否则标题里的中文全是方框,这个坑几乎每个人都踩过。
4.2 类型统计与年份趋势
# 图3:电影类型 Top10 条形图 fig, axes = plt.subplots(1, 2, figsize=(14, 5)) genre_counts.head(10).plot(kind='barh', ax=axes[0], color='mediumseagreen') axes[0].set_title('电影类型 Top10') axes[0].set_xlabel('数量') # 图4:按年份的电影产量趋势 yearly_count_filtered = yearly_count[(yearly_count.index >= 1980) & (yearly_count.index <= 2023)] axes[1].plot(yearly_count_filtered.index, yearly_count_filtered.values, color='tomato', linewidth=2) axes[1].set_title('电影产量年度趋势') axes[1].set_xlabel('年份') axes[1].set_ylabel('电影数量') plt.tight_layout() plt.savefig('genre_and_trend.png', dpi=150) plt.show()barh是水平条形图,适合类型名称较长的情况。年份趋势图里我加了 1980 到 2023 的过滤,因为早期数据往往不完整,画进去反而干扰判断。这些过滤条件不是固定的,你可以根据实际数据分布调整。
4.3 从 Notebook 到脚本的迁移要点
资源包里同时提供了.ipynb和.py两个版本,这不是凑数。Notebook 适合探索阶段,可以一段一段跑、随时看中间结果;脚本适合最终交付,能一键运行、方便集成。迁移时注意几点:把display()换成print(),把%matplotlib inline去掉,把分散的单元格合并成函数。
# 脚本版的组织方式:把分析逻辑封装成函数 def load_and_clean(path): df = pd.read_csv(path, encoding='utf-8') df = df.dropna(subset=['budget', 'revenue']) df['release_date'] = pd.to_datetime(df['release_date'], errors='coerce') df['release_year'] = df['release_date'].dt.year return df def plot_distributions(df, save_dir='.'): fig, ax = plt.subplots(figsize=(8, 5)) ax.hist(df['vote_average'].dropna(), bins=30) ax.set_title('评分分布') fig.savefig(f'{save_dir}/rating_dist.png', dpi=150) plt.close(fig) if __name__ == '__main__': data = load_and_clean('movies.csv') plot_distributions(data)这种写法比 Notebook 更适合放进毕设的代码附录里,老师一看就知道你有工程意识。plt.close(fig)是防止内存泄漏的习惯写法,批量生成图表时尤其重要。
5. 避坑与排查:那些跑不通时该看的地方
5.1 编码报错:UnicodeDecodeError
现象:pd.read_csv()直接抛UnicodeDecodeError: 'utf-8' codec can't decode byte...。
原因:CSV 文件不是 UTF-8 编码,常见的是 GBK 或 Latin-1,尤其在国内平台下载的数据集。
解决:先试encoding='gbk',再试encoding='latin-1'。如果都不行,用chardet库检测:import chardet; chardet.detect(open('movies.csv','rb').read())。
5.2 中文字体显示为方块
现象:图表标题和轴标签里的中文全是方框。
原因:matplotlib 默认字体不支持中文。
解决:在绘图前设置plt.rcParams['font.sans-serif'] = ['SimHei'](Windows)或['Arial Unicode MS'](macOS)。如果系统没有 SimHei,换成Microsoft YaHei也行。设置完记得加plt.rcParams['axes.unicode_minus'] = False,否则负号也会显示异常。
5.3 除零导致 inf 或 NaN
现象:计算 ROI 或增长率时出现inf或大量NaN。
原因:分母为 0 或缺失。
解决:用apply加条件判断,或者用np.where:df['roi'] = np.where(df['budget'] > 0, df['revenue'] / df['budget'], np.nan)。算完之后用df['roi'].replace([np.inf, -np.inf], np.nan)再兜底一次。
5.4 explode 之后行数暴涨
现象:拆分 genres 后数据行数从几千变成几万,后续统计全乱了。
原因:explode会把每个类型拆成独立行,一部电影有 3 个类型就变成 3 行。
解决:这是正常行为,但你要清楚后续统计的口径。如果只想统计类型分布,用 explode 后的数据没问题;如果要算每部电影的平均票房,得用 explode 前的数据。两种口径不要混用。
5.5 Notebook 能跑但脚本报错
现象:.ipynb里一切正常,换成.py就各种NameError。
原因:Notebook 的单元格共享全局命名空间,变量顺序执行;脚本是自上而下执行,变量定义顺序错了就会报错。
解决:把脚本里的函数定义放在调用之前,把import统一放在文件开头。用if __name__ == '__main__':包住主逻辑,避免导入时自动执行。
6. 进阶技巧:把分析结果做成可复用的报告
6.1 用 pandas profiling 快速生成数据概览
如果你不想手写一堆describe()和缺失值统计,可以用ydata-profiling(原 pandas-profiling)一键生成 HTML 报告。
# 安装:pip install ydata-profiling from ydata_profiling import ProfileReport profile = ProfileReport(df_clean, title='电影数据集概览', explorative=True) profile.to_file('movie_report.html')这份报告会自动包含每列的分布、缺失、相关性热力图,适合放在毕设附录里作为数据说明。缺点是数据量大时生成较慢,几万行以内没问题。
6.2 把关键结论导出为 Markdown 表格
答辩时老师往往没时间看你的 Notebook,一份简洁的结论表格比什么都管用。
# 汇总关键指标 summary = { '指标': ['电影总数', '平均评分', '平均片长(分钟)', '总票房(亿美元)', '评分最高年份'], '值': [ len(df_clean), round(df_clean['vote_average'].mean(), 2), round(df_clean['runtime'].mean(), 1), round(df_clean['revenue'].sum() / 1e8, 2), int(df_clean.groupby('release_year')['vote_average'].mean().idxmax()) ] } summary_df = pd.DataFrame(summary) summary_df.to_markdown('summary.md', index=False) print(summary_df.to_string(index=False))to_markdown需要tabulate库支持,pip install tabulate即可。导出的 Markdown 表格可以直接粘进论文或答辩 PPT。
6.3 参数化你的分析脚本
最后一个技巧:把文件路径、年份范围、Top N 这些变量抽到脚本开头,方便换数据集时快速调整。
# 配置区:换数据集时只改这里 CONFIG = { 'data_path': 'movies.csv', 'encoding': 'utf-8', 'year_range': (1980, 2023), 'top_n': 10, 'output_dir': './output' } # 使用配置 df = pd.read_csv(CONFIG['data_path'], encoding=CONFIG['encoding']) year_mask = (df['release_year'] >= CONFIG['year_range'][0]) & \ (df['release_year'] <= CONFIG['year_range'][1]) df_filtered = df[year_mask]这个习惯是我做了十几个数据分析项目之后才养成的。早期每次换数据都要在代码里翻半天改路径,后来统一抽成配置字典,省了大量时间。从那以后我每次拿到新数据集,第一件事就是先把配置区写好,再往下写分析逻辑。希望这个思路帮到你。
本文还有配套的精品资源,点击获取