Python 编程

pandas 进阶:清洗、分组与合并

14 分钟

真实数据从来不干净。数据科学里公认的规律:清洗数据的时间通常占整个项目的一半以上。


一、处理缺失值

df.isna().sum()                      # 先看清楚缺在哪

df.dropna()                          # 删掉有缺失的行
df.dropna(subset=["语文"])            # 只看这一列有没有缺

df["语文"].fillna(0)                  # 填 0
df["语文"].fillna(df["语文"].mean())  # 填均值
df["语文"].ffill()                    # 用前一个值填(时间序列常用)

怎么选?取决于缺失的原因

情况 建议
缺得很少(<5%) 删掉行最省事
缺失本身有含义(如「未填写」) 单独标记成一类,别乱填
数值型、随机缺失 填均值或中位数
时间序列 前向填充

一个重要提醒填均值会人为降低方差,让数据看起来比实际更集中。填之前想清楚代价。


二、处理重复和类型

df.duplicated().sum()        # 有多少完全重复的行
df = df.drop_duplicates()

df["分数"] = pd.to_numeric(df["分数"], errors="coerce")  # 转数字,转不了的变 NaN
df["日期"] = pd.to_datetime(df["日期"])

errors="coerce" 很实用:数据里混进了「暂无」「-」这类脏值时,它会把它们变成 NaN 而不是直接报错,然后你再统一处理缺失。


三、分组聚合:pandas 最强大的功能

「按某个列分组,对每组算一个统计量」——这是数据分析里最高频的操作。

df.groupby("班级")["成绩"].mean()              # 每个班的平均分
df.groupby("班级")["成绩"].agg(["mean", "max", "count"])   # 多个统计量
df.groupby(["班级", "性别"])["成绩"].mean()     # 按两个维度分组

分组的思维模型:拆分 → 应用 → 合并(split-apply-combine)

原表 → 按班级拆成几张小表 → 每张算平均 → 结果拼回一张表

理解了这个模型,groupby 的各种用法就都通了。

透视表(本质是 groupby 的表格化展示):

df.pivot_table(values="成绩", index="班级", columns="科目", aggfunc="mean")

四、合并多张表

pd.concat([df1, df2])                              # 纵向堆叠(行拼接)
pd.merge(df1, df2, on="学号", how="left")          # 按键关联(类似 SQL JOIN)

how 的四种取值

how 保留
inner 只保留两边都有的(默认)
left 保留左表全部
right 保留右表全部
outer 两边全保留

最常见的错误:默认用了 inner,结果数据悄悄变少了。

养成习惯:merge 之后立刻检查行数

print(len(df1), len(merged))    # 数量对不上就要查原因

五、一个高频警告:SettingWithCopyWarning

sub = df[df["语文"] > 85]
sub["等级"] = "优秀"          # ⚠️ 可能改不动原数据,也可能意外改了

原因sub 可能是原表的视图也可能是副本,pandas 自己也不确定,所以警告你。

正确写法

sub = df[df["语文"] > 85].copy()    # 明确要副本
sub["等级"] = "优秀"

# 或者直接在原表上改
df.loc[df["语文"] > 85, "等级"] = "优秀"

记住:看到这个警告不要忽略它,它经常对应着「我以为改了但其实没改」的 bug。

练习:给一份含班级、科目、成绩的数据,用 groupby 算出「每个班每科的平均分」,再用 pivot_table 展示成班级为行、科目为列的表格。

小纸条

groupby 的思维模型是什么?merge 之后为什么要立刻检查行数?SettingWithCopyWarning 怎么正确处理?

登录 后可看答案