一、可视化实现(真实源码 + 运行输出)
可视化代码集中在数据可视化处理.ipynb,本文只挑核心片段贴出来,图片全部是程序实际运行输出的 PNG(dpi=300)。
1.1 环境配置:中文字体是第一个坑
import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl import seaborn as sns from matplotlib.font_manager import FontProperties # ===================== 中文字体配置 ===================== font_path = "C:/Windows/Fonts/simhei.ttf" font_prop = FontProperties(fname=font_path) mpl.rcParams["font.family"] = ["SimHei", "Microsoft YaHei"] mpl.rcParams["axes.unicode_minus"] = False plt.style.use("seaborn-v0_8-whitegrid") ===================== 全局配置 ===================== SAVE_PATH = r"...\可视化处理\可视化图片" def save_fig(fig_name): plt.tight_layout() plt.savefig(f"{SAVE_PATH}\{fig_name}.png", dpi=300, bbox_inches="tight") plt.close()踩坑提醒:Windows 下 Matplotlib 默认字体不含中文,不设置的话标题和图例全是方框;axes.unicode_minus = False是为了让负号正常显示。
1.2 数据加载与预处理
df_total = pd.read_csv("...\清洗后数据集\douban_total_clean.csv") df_top250 = pd.read_csv("...\清洗后数据集\douban_top250_clean.csv") 1. 类型拆分:一部电影可能有多个类型(用空格分隔) df_type_expand = df_total.dropna(subset=["类型"]).copy() df_type_expand["类型"] = df_type_expand["类型"].str.split(" ") type_expand = df_type_expand.explode("类型") 2. 年代标签映射为大致年份 year_map = { "更早": 1950, "60年代": 1965, "70年代": 1975, "80年代": 1985, "90年代": 1995, "2000年代": 2005, "2010年代": 2015, "2019": 2019, "2020": 2020, "2021": 2021, "2022": 2022 } df_total["上映年份_映射"] = df_total["年代标签"].map(year_map).fillna(2000).astype(int) 3. 四大社会时期划分 def get_period(year): if 1947 <= year <= 1991: return "冷战时期" elif 2007 <= year <= 2009 or year == 2020: return "经济危机时期" elif 2020 <= year <= 2022: return "疫情时期" elif (2001 <= year <= 2007) or (2010 <= year <= 2019): return "世界经济上行时期" else: return "其他时期" df_total["社会时期"] = df_total["上映年份_映射"].apply(get_period) df_period = df_total[df_total["社会时期"] != "其他时期"].copy()1.3 总数据集五连图:高分电影的普遍规律
① 年代数量与评分趋势(双轴图)
time_analysis = df_total.groupby("年代标签").agg( 电影数量=("评分", "count"), 平均评分=("评分", "mean") ).reset_index() fig, ax1 = plt.subplots(figsize=(14, 6)) ax1.bar(time_analysis["年代标签"], time_analysis["电影数量"], color="#4ECDC4", alpha=0.8) ax1.set_ylabel("电影数量") ax2 = ax1.twinx() ax2.plot(time_analysis["年代标签"], time_analysis["平均评分"], color="#FF6B6B", marker="o", linewidth=3) ax2.set_ylabel("平均评分") plt.title("高分电影年代数量与评分趋势") save_fig("01_总数据集_年代趋势分析")② 地区占比 + 评分对比
region_count = df_total["地区"].value_counts() region_score = df_total.groupby("地区")["评分"].mean().sort_values(ascending=False) fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6)) ax1.pie(region_count, labels=region_count.index, autopct="%.1f%%", colors=["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FECA57"]) ax1.set_title("高分电影地区占比") sns.barplot(x=region_score.index, y=region_score.values, ax=ax2) ax2.set_title("各地区平均评分对比") save_fig("02_总数据集_地区分布分析")③ TOP15 电影类型分布
type_count = type_expand["类型"].value_counts().head(15) plt.figure(figsize=(12, 8)) sns.barplot(y=type_count.index, x=type_count.values, palette="viridis") plt.title("高分电影TOP15类型分布") for i, v in enumerate(type_count.values): plt.text(v + 5, i, str(v), va="center") save_fig("03_总数据集_类型分布分析")④ 评分分布
plt.figure(figsize=(10, 6)) sns.histplot(df_total["评分"], bins=15, kde=True, color="#5470C6") plt.title("高分电影评分分布") plt.xlabel("豆瓣评分") plt.ylabel("电影数量") save_fig("04_总数据集_评分分布分析")⑤ 四大社会时期:类型偏好 + 评分对比
# 先按时期拆类型,统计各时期 TOP5 类型 df_period_type = df_period.dropna(subset=["类型"]).copy() df_period_type["类型"] = df_period_type["类型"].str.split(" ") df_period_type = df_period_type.explode("类型") period_type_list = [] for period in df_period_type["社会时期"].unique(): subset = df_period_type[df_period_type["社会时期"] == period] for t, c in subset["类型"].value_counts().head(5).items(): period_type_list.append({"社会时期": period, "类型": t, "电影数量": c}) period_type_top5 = pd.DataFrame(period_type_list) plt.figure(figsize=(16, 8)) sns.barplot(data=period_type_top5, x="类型", y="电影数量", hue="社会时期", palette="Set2") plt.title("四大社会经济时期高分电影TOP5类型对比") save_fig("05_总数据集_四大时期类型分析") 各时期平均评分 period_score = df_period.groupby("社会时期")["评分"].mean().sort_values(ascending=False) plt.figure(figsize=(10, 6)) sns.barplot(x=period_score.index, y=period_score.values, palette="coolwarm") plt.title("四大社会经济时期高分电影平均评分对比") save_fig("06_总数据集_四大时期评分分析")结论(这是全篇最让我意外的部分):
| 时期 | 观众偏好 | 平均评分 |
|---|---|---|
| 冷战时期(1947–1991) | 剧情、犯罪、战争、科幻(军备竞赛、太空竞赛的时代写照) | 8.92 |
| 经济危机时期(2007–2009、2020) | 喜剧、温馨、治愈、动画(逃避现实、寻求放松) | 8.65 |
| 疫情时期(2020–2022) | 剧情、喜剧、温情、悬疑(居家观影,流媒体爆发) | 8.58 |
| 经济上行时期(2001–2007、2010–2019) | 科幻、动作、冒险、动画齐发力(工业化 + 多元化) | 8.73 |
一句话总结:社会越紧张,观众越想看治愈的内容;社会越繁荣,类型越百花齐放。
7.4 Top250 三连图:顶级口碑电影是怎么炼成的
① 高频导演平均评分
director_score = df_top250.groupby("导演")["评分"].agg( ["count", "mean"] ).sort_values(by="count", ascending=False).head(10) plt.figure(figsize=(12, 6)) sns.barplot(x=director_score.index, y=director_score["mean"], palette="coolwarm") plt.title("Top250高频导演平均评分对比") plt.xticks(rotation=45) plt.ylabel("平均评分") save_fig("07_Top250_导演分析")② 主演与评分关联(Plotly 交互散点图)
import plotly.express as px fig = px.scatter(df_top250, x="主演", y="评分", hover_name="电影名称", title="Top250主演-评分关联") fig.update_layout(height=500, xaxis={"tickangle": 45}) fig.show()③ 类型、地区、评分分布三件套
# Top250 类型分布 top250_type = df_top250.dropna(subset=["类型"]).copy() top250_type["类型"] = top250_type["类型"].str.split(" ") top250_type_count = top250_type.explode("类型")["类型"].value_counts().head(10) plt.figure(figsize=(10, 6)) sns.barplot(x=top250_type_count.index, y=top250_type_count.values) plt.title("Top250电影TOP10类型分布") save_fig("09_Top250_类型特征分析") Top250 地区占比 + 评分 top250_region = df_top250["地区"].value_counts() top250_region_score = df_top250.groupby("地区")["评分"].mean() fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6)) ax1.pie(top250_region, labels=top250_region.index, autopct="%.1f%%") ax1.set_title("Top250电影地区占比") sns.barplot(x=top250_region_score.index, y=top250_region_score.values, ax=ax2) ax2.set_title("Top250各地区平均评分") save_fig("10_Top250_地区特征分析") Top250 评分分布 plt.figure(figsize=(10, 6)) sns.histplot(df_top250["评分"], bins=10, kde=True, color="#96CEB4") plt.title("Top250电影评分分布") save_fig("11_Top250_评分分布特征")