基于上一步搭建 网络机器人 抓取的数据我们进行下一步的数据分析。
如果你刚听到"数据分析"觉得高深莫测,别怕!今天我们将通过一个真实的电影案例,带你一步步拆解代码,浅浅体验一下 Python 数据分析界的大明星------Pandas。
我们的目标很清晰:读入一份电影数据,洗一洗脏数据,然后画出 4 张图,看看电影圈到底藏着什么秘密!
第一步:把数据"端上桌" ------ 数据读取
做饭得先买菜,分析数据得先把数据读进来。Pandas 最擅长的事情之一,就是把各种格式的文件变成它认识的对象------ DataFrame(数据框)。你可以把它想象成一张超级加强版的 Excel 表格。
python
import pandas as pd
import matplotlib.pyplot as plt
def main():
# 读取数据
# pd.read_csv: Pandas的读文件神器,一行代码把CSV变成DataFrame
# usecols: 就像在餐厅点菜,原表格列很多,我们只提取需要分析的这7列,省时省力
# dtype: 提前告诉Pandas,"年份"这列请用Int64(整数)类型对待,不要当成纯文本
df = pd.read_csv('data/movie.csv',
usecols=["电影名","年份","上映时间","类型","时长","评分","语言"],
dtype={"年份": "Int64"})
第二步:给数据"洗个澡" ------ 缺失值处理
现实中的数据往往很脏,比如有些老电影连"年份"都没填。如果我们想看历年电影数量变化,有空缺可不行。
我们的策略是:如果"年份"为空,就从"上映时间"(如 1994-01-01)里截取前 4 位作为替补。
python
# 展示中文: 如果不设置这行,图表里的中文标题会变成方块口口口
plt.rcParams['font.sans-serif'] = ['SimHei']
# 创建画布: nrows=2, ncols=2 表示画一个2行2列的网格,放4张图
# figsize=(15, 10) 设置画布宽15英寸,高10英寸
# gridspec_kw={'hspace': 0.5, 'wspace': 0.4} 设置子图之间的高度和宽度留白,防止挤在一起
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(15, 10), gridspec_kw={'hspace': 0.5, 'wspace': 0.4})
# 设置整个大画布的顶部大标题
fig.suptitle('电影数据统计', fontsize=16, x=0.5, y=0.92)
# axes是一个2x2的数组,我们分别把它们取出来,方便后面画图时调用
axes1 = axes[0, 0] # 第1行第1列
axes2 = axes[0, 1] # 第1行第2列
axes3 = axes[1, 0] # 第2行第1列
axes4 = axes[1, 1] # 第2行第2列
# --- 数据清洗:处理年份缺失 ---
# astype(str): 先把上映时间列转成字符串格式
# .str[:4]: 使用Pandas的字符串切片方法,截取前4个字符(即年份,如"1994")
extracted_year = df['上映时间'].astype(str).str[:4]
# fillna: 神奇的填充函数。它自动检查df['年份'],哪里是空,就用对应行的extracted_year填上;哪里有值,就保留原样
df['年份'] = df['年份'].fillna(extracted_year)
💡 Pandas 哲学初体验:向量化操作 在传统编程中,你想对一列数据做处理,得写
for循环挨个判断。但在 Pandas 里,你直接对整列(Series)下达指令(如.fillna),它会在底层瞬间帮你搞定所有行,这就叫"向量化",不仅代码优雅,速度还快得飞起!
第三步:让数据"开口说话" ------ 统计与可视化
数据洗干净了,接下来进入正题:统计画图。
📈 图 1:历年电影数量变化(折线图)
想知道电影产业是不是越来越繁荣?我们需要按年份统计电影数量,并确保年份是连续的。
python
# --- 图1:折线图 ---
# groupby('年份'): 把电影按年份分装进不同的"文件夹"
# ['电影名'].count(): 数一下每个"文件夹"里有几部电影
year_count = df.groupby('年份')['电影名'].count()
# year_count是一个带标签的Series,标签是年份,值是数量
# .index.min() 获取标签的最小值(最早年份),.index.max() 获取最大值(最新年份)
min_year = year_count.index.min()
max_year = year_count.index.max()
# 生成X轴数据:从最早年份到最新年份的连续整数列表,确保折线图时间轴不断裂
x = [i for i in range(min_year, max_year + 1)]
# 生成Y轴数据:遍历连续的年份x,去year_count里取数量
# .get(i, 0) 的意思是:获取年份i的数量,如果找不到(说明这年没电影),就返回0填补
y = [int(year_count.get(i, 0)) for i in x]
# 绘制折线图:linestyle='-'表示实线,color='green'表示绿色
axes1.plot(x, y, linestyle='-', color='green')
axes1.set_title('每一年上映的电影数量的变化')
axes1.set_xlabel('年份')
axes1.set_ylabel('数量')
axes1.grid(True, linestyle='--', alpha=0.5) # 添加虚线网格,透明度0.5,方便对齐看数
# x[::8]: 列表切片步长为8,表示每隔8个年份才显示一个刻度,防止X轴挤满数字
axes1.set_xticks(x[::8])
# range(0, 31, 3): 从0到30,步长为3,表示每隔3个数量显示一个刻度
axes1.set_yticks([i for i in range(0, 31, 3)])
📊 图 2:不同语言电影数量(柱状图)
这次我们用更简练的 Pandas 风格一步到位:
python
# --- 图2:柱状图 ---
# 分组、计数、画图一气呵成!
# kind='bar': 画柱状图
# ax=axes2: 把图画到第2个子图区域
df.groupby('语言')['电影名'].count().plot(kind='bar', ax=axes2)
axes2.set_title('不同语言电影数量')
axes2.set_xlabel('语言')
axes2.set_ylabel('数量')
axes2.grid(True, linestyle='--', alpha=0.5)
📊 图 3:不同类型电影数量(嵌套数据的破局之法)
图 2 的语言很简单,因为一部电影通常只有一种语言。但"类型"不同,一部电影往往是 "剧情,动作,科幻" 混合体。如果直接用 value_counts,Pandas 会把整个字符串当成一种类型。
这时候,我们不得不暂时告别向量化,请出基础的 for 循环来拆分数据:
python
# --- 图3:柱状图(多标签拆分) ---
count_types={}
# 第一层循环:遍历df['类型']的每一行,type是一个字符串,如"剧情,动作"
for type in df['类型']:
# 第二层循环:用split(',')按逗号把字符串拆成列表,遍历每一个具体类型
for t in type.split(','):
# 如果这个类型t还没在字典里,初始化为1;如果已经有了,数量+1
if t not in count_types:
count_types[t] = 1
else:
count_types[t] += 1
# 画柱状图:X轴是字典的键,Y轴是字典的值
axes3.bar(count_types.keys(), count_types.values())
axes3.set_title('不同类型电影数量')
axes3.set_xlabel('类型')
# 必须先设置刻度位置,再设置刻度标签!
# range(len(count_types)): 生成0,1,2...的刻度位置
axes3.set_xticks(range(len(count_types)))
# rotation=45: 标签旋转45度,防止文字重叠
axes3.set_xticklabels(count_types.keys(), rotation=45)
axes3.set_ylabel('数量')
axes3.grid(True, linestyle='--', alpha=0.5)
何时用循环? 当数据格式本身需要"拆解重组",且 Pandas 没有提供直接的向量化方法时,回归循环是务实的选择。
🍕 图 4:电影评分比例(饼图与数据合并)
评分种类太多,饼图会密密麻麻看不清。我们需要把占比极小的评分合并为"其他"。
python
# --- 图4:饼状图 ---
# value_counts(): 专门用来数各类值出现次数的统计神器
score_counts = df['评分'].value_counts()
# 计算比例:每类数量除以总数量
score_percentage = score_counts / score_counts.sum()
# 条件筛选:score_percentage < 0.05 会返回一串True/False
# 用它去切片Series,挑出占比小于5%的项,并取出它们的索引(即具体评分值)
other_scores = score_percentage[score_percentage < 0.05].index
# apply + lambda: Pandas里的"微创手术"
# lambda是匿名函数,这行的意思是:遍历每行的评分x,如果x在小比例名单里,就改成"其他",不然保留原样
df['处理后评分'] = df['评分'].apply(lambda x: '其他' if x in other_scores else x)
# 对处理后的新列重新统计数量
processed_counts = df['处理后评分'].value_counts()
# 画饼图:autopct='%1.1f%%' 表示显示百分比,保留1位小数
axes4.pie(processed_counts, labels=processed_counts.index, autopct='%1.1f%%')
axes4.set_title('各个电影评分的比例')
# loc='lower center': 图例放在底部中间;bbox_to_anchor: 精细调整图例的位置,防止挡住饼图
axes4.legend(loc='lower center', ncol=5, bbox_to_anchor=(0.5, -0.2))
第四步:收尾保存
图表画完了,最后一步是把成果保存下来,并展示给用户看。
python
# 将绘制好的4合1图表保存为当前目录下的png图片
plt.savefig('movie.png')
# 在屏幕上弹出窗口展示图表
plt.show()
# Python的标准入口写法:只有当直接运行这个脚本时,才会执行main()函数
if __name__ == '__main__':
main()

总结
回顾这份代码,我们经历了:
- 读取:将杂乱文件变成规整的 DataFrame。
- 清洗 :用
fillna搞定缺失值。 - 分析 :用
groupby和value_counts从不同维度透视数据,遇到复杂拆分灵活运用循环。 - 展现:用 Matplotlib 将枯燥的数字变成直观的折线、柱状和饼图。
这就是 Pandas 的魅力所在:它让你能像拼积木一样,用极少的代码完成从数据到洞察的完整闭环。希望这次浅浅的体验,能为你推开数据分析的大门!