
【三杰与四斯逐集对比】至急用 Python 拆解镰仓场景与不死的格罗扎姆剧集数据差异三杰与四斯的逐集对比如果只用哪一集更好看哪个怪兽更霸气来争论最后往往会变成各说各话。标题里那个至急本质上是想快速拿到一份可量化的对比结论镰仓作为剧情场景和不死的格罗扎姆作为登场强敌到底谁对剧集评分与热度的贡献更大这两组要素叠加在一起时会不会产生112的效果这篇教程不讨论粉丝圈里对三杰四斯具体归属的争论而是把它抽象成一个很典型的剧集数据分析需求两组剧集集合多个字段维度逐集记录评分、热度、登场角色、场景地点然后按要素分组做交叉对比。这个套路在影视剧分析、长视频内容运营、甚至综艺节目集数复盘里都能通用。我会从环境搭建开始给出一个可以直接复制运行的 Python 数据分析脚本包含数据清洗、特征加工、分组统计、可视化、报告导出五个环节最后补充高频报错排查和工程化建议。代码全部基于 pandas、numpy、matplotlib不需要引入重型的分析平台一台普通开发机就能跑完。1. 背景与核心概念1.1 真正要解决的问题是什么逐集对比这个词拆开看包含两层含义一是逐集即数据粒度要到达每一集不能只拿整部作品的平均值说事二是对比即至少要有一个对比基准把两组剧集集合放在同一个指标体系下做横向比较。以镰仓VS不死的格罗扎姆为例可以把问题进一步拆成三个可统计的命题第一以场景为分组维度。镰仓登场的剧集评分均值和非镰仓剧集差异有多大热度均值是否更高这说明场景在吸引观众注意力方面有没有显著作用。第二以角色登场为分组维度。格罗扎姆登场的剧集评分与其他怪兽登场的剧集相比是高是低这可能关系到角色的压迫感、剧情张力以及观众对这个角色的期待值。第三做交叉对比。镰仓场景和格罗扎姆同时出现的剧集是否比只有其中一个要素出现的剧集表现更好这种同时命中多个卖点的分析在内容运营里非常常见。要回答这些问题不能靠印象得先把每一集的数据结构化。这是逐集对比分析最开始也最关键的一步。1.2 为什么做逐集对比不能只看平均值很多观众讨论作品时会说整体水平不错平均分挺高。但平均值很容易掩盖剧集内部的波动。一个系列可能前半段评分 9 分后半段跌到 6 分平均分 7.5看起来不算差但如果要和另一个全程稳定的系列对比就直接高下立判了。逐集对比的价值就在于保留剧集之间的波动信息让趋势、拐点、异常集都能暴露出来。具体到这篇教程我们需要关注三个不同层级的输出作品层三杰系列和四斯系列各自的总集数、平均评分、热度均值。要素层镰仓场景是否出现、格罗扎姆是否登场这两类布尔条件对指标的影响。趋势层每一集的评分和热度在系列内的走势是否存在前高后低或后期发力。这三个层级分别对应数据分析里的整体统计、分组聚合和趋势可视化一套流程全部覆盖。1.3 技术方案概览实现这套逐集对比分析我选择 Python 生态里最常见的三个库pandas 负责数据表的读取、清洗、分组和聚合。numpy 负责生成模拟数据、处理数值计算。matplotlib 负责把对比结果绘制成图表。选择这三个库的原因很直接它们安装简单学习曲线平缓而且示例代码可以无缝迁移到 Jupyter Notebook、VS Code 或者直接写成 .py 脚本里执行。如果后续想扩展成自动化报表也很容易和 Excel、Markdown 导出逻辑结合。2. 环境准备与项目初始化2.1 本机环境要求本文的代码在 Windows 10/11、macOS、主流 Linux 发行版上都可以运行。唯一要注意的是 Python 版本不能太低建议使用 3.8 及以上版本。pandas、numpy、matplotlib 这三个库对新版 Python 的兼容性很好不需要额外折腾虚拟环境之外的编译工具。如果你不确定当前 Python 版本可以在命令行里先确认一下python --version如果输出结果是 Python 3.8.10 这样的版本号说明环境基本可用。如果是 Python 2.x或者 3.6 以下的老版本建议先升级 Python再继续后续安装依赖的操作。2.2 安装依赖库建议先创建一个独立的虚拟环境避免把依赖装到全局环境里造成版本冲突。创建虚拟环境的命令如下python -m venv toku_envWindows 系统激活虚拟环境toku_env\Scripts\activatemacOS 或 Linux 系统激活虚拟环境source toku_env/bin/activate激活之后安装本文需要使用的三个核心库pip install pandas numpy matplotlib如果你的 Python 环境已经安装过这些库可以加一个--upgrade参数确保版本不会太旧。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。2.3 项目目录结构为了让脚本保持清晰我建议按下面的目录结构组织文件tokusatsu-compare/ ├── data/ # 存放原始数据和处理后的数据 ├── output/ # 存放图表和报告 └── main.py # 主分析脚本其中data目录可以放原始剧集信息表output目录用来接收脚本生成的图表和报告。如果后续数据量变大还可以继续拆分出config目录存放分析配置。3. 数据模型与示例数据准备3.1 逐集数据字段设计逐集对比分析的第一步是设计一张规范化的剧集信息表。这张表的每一行代表一集每一列代表一个属性。我设计的字段如下字段名类型说明series字符串剧集所属系列示例使用三杰系列与四斯系列episode整数集数title字符串该集标题monster字符串本集登场怪兽或敌人格罗扎姆表示目标角色登场scene字符串主要战斗或剧情场景镰仓表示目标场景出现rating浮点数单集评分heat整数单集热度值本文用相对数值表示其中monster和scene是后续对比分析的关键分组字段。rating和heat则是被分析的数值指标。这里有一个重要的设计思路把登场怪兽和场景地点单独拆成两个字段而不是混在一个自由文本里。否则后续做分组统计时会非常痛苦既要处理字符串包含关系又要担心换行符、空格等干扰。结构化字段是数据分析的基础。3.2 生成演示数据由于这篇文章不是爬虫教程而且真实剧集数据往往涉及版权和渠道授权所以我直接在脚本里生成一组演示用的 Mock 数据。Mock 数据的价值在于读者不需要先去寻找数据源就能完整体验从清洗到可视化对比的整个流程。等流程跑通了再替换成自己手头的数据即可。代码如下先用np.random.seed(42)固定随机种子保证每次运行生成的数据一致方便对照结果。import numpy as np import pandas as pd # 固定随机种子保证结果可复现 np.random.seed(42) series_names [三杰系列, 四斯系列] episodes_per_series 15 records [] for series in series_names: for ep in range(1, episodes_per_series 1): record { series: series, episode: ep, title: f{series} 第{ep}话, monster: np.random.choice( [格罗扎姆, 其他怪兽, 无怪兽], p[0.30, 0.60, 0.10] ), scene: np.random.choice( [镰仓, 东京, 大阪, 冲绳], p[0.25, 0.45, 0.20, 0.10] ), rating: round( float(np.clip(np.random.normal(8.3, 0.7), 6.0, 9.8)), 1 ), heat: int( np.clip(np.random.normal(800000, 150000), 300000, 1200000) ), } records.append(record) df pd.DataFrame(records) print(df.head()) print(df.shape)运行这段代码后会输出一个 30 行 7 列的数据表。rating的分布会集中在 8.3 分附近heat的分布会集中在 80 万附近同时带有合理的随机波动。这里需要特别提醒以上数据全部是随机生成的演示数据不代表任何真实作品的评分与热度。真实分析时一定要用可信的数据源并且提前确认数据的使用权限。3.3 为什么要固定随机种子随机种子是一个容易被初学者忽略但实际工程中非常重要的细节。如果不设置np.random.seed(42)那么每次运行脚本生成的 Mock 数据都不一样。你今天看到格罗扎姆登场剧集评分更高明天可能就变成评分更低完全没法对分析结论做验证。固定随机种子之后随机数生成器的起点就确定了只要不改变随机函数的调用顺序每次生成的分布结果都一样。这在调试脚本、编写自动化报告、团队协作复现结论时是基本要求。4. 数据清洗与特征加工4.1 缺失值检查真实数据不会像 Mock 数据这样干净字段里可能出现空值、错误值、重复值。所以在做任何分组统计之前必须先把数据质量检查一遍。这里给出一个通用的检查思路# 检查每一列的缺失值数量 print(df.isna().sum()) # 检查完全重复的行 print(df.duplicated().sum()) # 查看数值列的基本统计信息 print(df.describe())如果是你的真实数据缺失值的处理策略取决于缺失比例。如果某列的缺失比例低于 5%可以直接删除对应行如果缺失比例较高比如scene字段有 30% 的空值就需要考虑是填充未知还是单独作为一个类别参与分析。本文的 Mock 数据没有缺失值所以这里只演示检查方法不实际删除数据。4.2 文本归一化数据清洗的另一个重点是文本字段的归一化。比如不死的格罗扎姆和格罗扎姆在字面上不同但在分析语义里是同一个角色。如果不做归一化groupby(monster)会把它们当成两个类别统计结果就会被错误地拆开。下面这段代码会去掉字符串首尾空格并把不死的格罗扎姆统一归一到格罗扎姆def normalize_monster(value): if pd.isna(value): return 未知 value str(value).strip() if value in (不死的格罗扎姆, 格罗扎姆): return 格罗扎姆 return value def normalize_scene(value): if pd.isna(value): return 未知 return str(value).strip() df[monster] df[monster].apply(normalize_monster) df[scene] df[scene].apply(normalize_scene) # 确认归一化之后的分组分布 print(df[monster].value_counts()) print(df[scene].value_counts())归一化要在分析之前完成最好把这段逻辑封装成单独的函数后续拿到新数据时可以直接复用。4.3 添加布尔特征列为了后续交叉对比更直观我建议新增两个布尔列is_grozam_episode表示本集是否格罗扎姆登场is_kamakura_scene表示本集场景是否包含镰仓。布尔列在groupby分组中的可读性更清晰也方便和 matplotlib 绘图逻辑对接。df[is_grozam_episode] df[monster] 格罗扎姆 df[is_kamakura_scene] df[scene] 镰仓 print(df.head())这一步之后数据表里已经有了干净的分类字段、数值字段和布尔标识字段可以进入核心的对比分析环节了。5. 核心对比分析镰仓场景 vs 格罗扎姆登场5.1 按场景维度统计第一个问题是镰仓场景登场的剧集评分和热度表现如何先用groupby按场景分组聚合出集数、平均评分、平均热度。scene_stats df.groupby(scene).agg( 集数(title, count), 平均评分(rating, mean), 热度均值(heat, mean), ).reset_index() scene_stats[平均评分] scene_stats[平均评分].round(2) scene_stats[热度均值] scene_stats[热度均值].astype(int) print(scene_stats)输出结果中镰仓那一行就会带出平均评分和热度均值。你可以清晰看到在所有场景中镰仓剧集的评分处于什么位置是否明显高于东京、大阪、冲绳。如果样本量足够大还能继续算置信区间但演示数据只有 30 集所以这里只做描述性统计。5.2 按角色登场维度统计第二个问题是格罗扎姆登场对剧集表现有没有影响。同样用groupby但分组字段换成is_grozam_episode。monster_stats df.groupby(is_grozam_episode).agg( 集数(title, count), 平均评分(rating, mean), 热度均值(heat, mean), ).reset_index() monster_stats[平均评分] monster_stats[平均评分].round(2) monster_stats[热度均值] monster_stats[热度均值].astype(int) print(monster_stats)从演示数据的逻辑来说如果格罗扎姆登场剧集的平均评分比未登场剧集高说明这个角色的登场确实能带来正向观感。但这只是一个相关关系并不是因果关系因为评分还可能受脚本、导演、同期其他作品竞争等因素影响。5.3 交叉对比与结论解读第三个问题最有意思镰仓场景和格罗扎姆登场同时命中的剧集表现会不会更好这里把is_kamakura_scene和is_grozam_episode两个布尔列放在一起分组。cross_stats df.groupby( [is_kamakura_scene, is_grozam_episode] ).agg( 集数(title, count), 平均评分(rating, mean), 热度均值(heat, mean), ).reset_index() cross_stats[平均评分] cross_stats[平均评分].round(2) cross_stats[热度均值] cross_stats[热度均值].astype(int) print(cross_stats)这张交叉统计表会有四行is_kamakura_sceneis_grozam_episode集数平均评分热度均值FalseFalse若干......FalseTrue若干......TrueFalse若干......TrueTrue若干......通过这四行的对比可以直观看到镰仓格罗扎姆的组合是否存在叠加效应。如果同时命中的剧集在评分和热度上都不是最高那说明两个卖点放在一起可能有资源竞争或叙事冲突这个结论对内容策划非常有参考价值。需要强调的是由于 Mock 数据是随机生成的这四行数据之间可能并没有稳定的规律。但在真实数据里这种交叉对比往往能发现很多有趣的模式。这也是逐集对比分析最有价值的输出之一。6. 逐集趋势可视化6.1 评分逐集走势图数据表格能告诉我们分组均值但丢失了剧集顺序带来的趋势信息。接下来用 matplotlib 画出每个系列的评分逐集走势图。import matplotlib.pyplot as plt # 解决中文显示问题 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(10, 5)) for series_name, group in df.groupby(series): ax.plot( group[episode], group[rating], markero, labelseries_name, ) ax.set_xlabel(集数) ax.set_ylabel(评分) ax.set_title(三杰系列 vs 四斯系列逐集评分走势对比) ax.legend() ax.grid(True, linestyle--, alpha0.5) plt.tight_layout() plt.savefig(output/episode_rating_trend.png, dpi150) plt.show()如果图表中文字体出现方块乱码说明当前系统缺少代码里指定的中文字体。Windows 一般自带 SimHeimacOS 可以用 PingFang SCLinux 用户可能需要安装文泉驿或者 Noto Sans CJK。6.2 镰仓场景与格罗扎姆登场分组柱状图趋势图适合看整体走势分组柱状图更适合直接对比两个要素的贡献。这里把是否镰仓场景和是否格罗扎姆登场作为横轴标签分别展示评分均值。fig, axes plt.subplots(1, 2, figsize(12, 5)) # 按是否镰仓场景分组 scene_group df.groupby(is_kamakura_scene)[rating].mean() axes[0].bar( [非镰仓, 镰仓], scene_group.values, color[#6b8ea8, #c97b7b], ) axes[0].set_ylim(6, 10) axes[0].set_title(镰仓场景 vs 非镰仓场景平均评分) axes[0].set_ylabel(平均评分) # 按是否格罗扎姆登场分组 monster_group df.groupby(is_grozam_episode)[rating].mean() axes[1].bar( [格罗扎姆未登场, 格罗扎姆登场], monster_group.values, color[#6b8ea8, #c97b7b], ) axes[1].set_ylim(6, 10) axes[1].set_title(格罗扎姆登场 vs 未登场平均评分) plt.tight_layout() plt.savefig(output/group_compare.png, dpi150) plt.show()柱状图把分类变量的组间差异可视化之后人眼可以很快判断差异大小。不过要注意柱子的高度差异在视觉上有放大效应如果两组均值只差 0.1看起来也可能很明显需要结合实际数值做判断。6.3 热度与评分相关性热力图最后画一张热力图看热度、评分、集数这几个数值列之间的相关性。虽然集数是递增序列把它和相关分析放进来主要为了演示方法实际分析时可以去掉。# 计算数值列的相关矩阵 numeric_df df[[episode, rating, heat]] corr_matrix numeric_df.corr() # 绘制热力图 fig, ax plt.subplots(figsize(6, 5)) im ax.imshow(corr_matrix, cmapcoolwarm, vmin-1, vmax1) ax.set_xticks(range(len(corr_matrix.columns))) ax.set_yticks(range(len(corr_matrix.columns))) ax.set_xticklabels(corr_matrix.columns) ax.set_yticklabels(corr_matrix.columns) # 在格子里标注数值 for i in range(len(corr_matrix.columns)): for j in range(len(corr_matrix.columns)): text f{corr_matrix.iloc[i, j]:.2f} ax.text(j, i, text, hacenter, vacenter, colorblack) plt.title(逐集对比热度与评分相关性热力图) plt.tight_layout() plt.savefig(output/corr_heatmap.png, dpi150) plt.show()如果rating和heat的相关系数为正且比较大说明叫好又叫座如果接近零说明口碑和热度并不直接挂钩。这个信息在宣传排期、内容分发策略里都有参考价值。7. 一键导出对比报告与常见问题7.1 导出 Excel 与 Markdown 报告分析做完之后把结果导出成文件可以方便团队其他人查阅。pandas 的to_excel可以把多张统计表导出到同一个工作簿的多个 Sheet 中。先确保安装了 openpyxlpip install openpyxlwith pd.ExcelWriter(output/tokusatsu_compare.xlsx) as writer: df.to_excel(writer, sheet_name逐集明细, indexFalse) scene_stats.to_excel(writer, sheet_name场景对比, indexFalse) monster_stats.to_excel(writer, sheet_name角色登场对比, indexFalse) cross_stats.to_excel(writer, sheet_name交叉对比, indexFalse)同时还可以生成一个简单的 Markdown 报告方便直接粘贴到文档或博客里。Markdown 表格可以手工拼接不依赖额外库def dataframe_to_markdown(df): headers | | .join(map(str, df.columns)) | separator | |.join([---] * len(df.columns)) | lines [headers, separator] for _, row in df.iterrows(): lines.append(| | .join(map(str, row.values)) |) return \n.join(lines) report_lines [ # 三杰与四斯逐集对比报告, , ## 交叉对比表, , dataframe_to_markdown(cross_stats), , ] with open(output/report.md, w, encodingutf-8) as f: f.write(\n.join(report_lines)) print(报告已导出到 output/report.md)这里要注意 Excel 的列和 Sheet 名称不要使用特殊字符否则写入会报错。7.2 常见问题排查表在编写和执行脚本过程中有几个高频问题需要重点关注。我把它们的现象、原因和解决方案整理成一个表问题现象常见原因解决思路图表中文显示为方块系统缺少中文字体或字体配置不正确根据操作系统选择 SimHei、PingFang SC、Noto Sans CJK 并重新设置 rcParamsto_excel报错 ModuleNotFoundError未安装 openpyxl执行pip install openpyxl后再运行groupby结果里出现 NaN原始文本字段有空值先执行isna().sum()检查按策略填充或删除评分范围异常比如超过 10随机数生成后未做边界截断使用np.clip把数值限制在合理性范围内图表尺寸过大或过小figsize 参数设置不当按画布内容量调整figsize(宽, 高)一般 10:5 或 12:5 比较合适排查问题时建议每次只改一个变量运行一次观察输出。不要一次性修改多个参数否则很难确定问题来源。7.3 真实数据采集合规提示如果之后要将 Mock 数据替换为真实剧集数据请务必注意数据来源的合规性。优先使用官方公开接口、已获授权的数据集或者人工整理可公开查询的信息。切勿对视频网站、百科站点做高频爬虫更不要尝试绕过反爬机制。数据采集和分析的目的是辅助内容研究不应该触碰版权或服务条款红线。另外产出图表和数据表格后如果要公开发布同样要留意素材版权和商标规范。剧集标题、截图、海报属于作品的版权方本文演示只保留了文本字段和统计数字这是相对安全的做法。8. 最佳实践与工程化建议8.1 让脚本可复用函数化与参数化上面所有代码都在一个流程里从上到下执行适合快速验证。但如果想把这套逐集对比分析变成一个每周可重复运行的工具就需要做函数化改造。我建议至少拆成四个函数load_and_clean(df_or_path)负责加载数据、清洗文本、填充缺失值。compute_group_stats(df, group_cols, value_cols)负责分组聚合返回统计表。plot_compare(df, output_dir)负责所有图表绘制。export_report(df, stats_list, output_dir)负责导出 Excel 和 Markdown 报告。参数化之后新增一个系列的数据只需要改数据文件路径和系列名称字段不需要改动分析逻辑。这比每次都复制粘贴脚本要可靠得多。8.2 区分相关性与因果性逐集对比分析得出的是相关性不是因果性。比如镰仓场景剧集平均分更高可能是因为这几个剧集本身脚本更好也可能是因为镰仓场景对观众有特殊吸引力。在做业务决策时建议结合集数标题、导演、编剧等多维信息做深度归因或者用更严格的实验设计来验证。在报告呈现上我也建议不要写镰仓拉升了评分而是写本数据集中镰仓场景剧集的平均评分高于非镰仓场景剧集。这个表述在逻辑上更严谨。8.3 扩展方向与后续学习这套对比分析框架还有很多可以扩展的方向。第一把文本字段做细粒度标签化比如把怪兽拆成力量型敏捷型不死型把场景拆成城市废墟海边地下基地这样能更细致地定位高分集共性。第二引入时间维度。如果数据里有每集的播出日期可以分析评分是否存在季节性波动或者同一角色登场集数的间隔时间对热度的影响。第三把静态报告升级成 Dashboard。在不引入重框架的前提下可以先用matplotlib生成多张图再用 HTML 模板拼成一份带交互筛选的静态页面用pyscript或者纯前端方案打开。第四对结论做置信度评估。当分组样本量太小时均值差异可能只是随机波动。可以引入scipy.stats的ttest_ind做两独立样本 t 检验只有 p 值小于 0.05 时才说差异有统计意义。不过这个操作要求数据量足够且满足正态性假设不能盲目套用。如果你后续想深入学习可以继续围绕 pandas 的groupby聚合技巧、matplotlib 的图表美化、以及数据报告自动化这三个方向做练习。掌握这套技能后不光能分析特摄剧集任何带剧集属性的长视频内容比如电视剧、综艺、动画都能用同样的代码完成逐集对比分析。本文的完整示例代码可以直接复制运行Mock 数据保证了你不需要先找数据源就能验证整个流程。如果你手头有真实的剧集数据把records构造部分替换成pd.read_csv或pd.read_excel即可后面的清洗、分组、可视化和导出逻辑完全不需要改动。