学径XUEJING · 个人学习平台通知设置

课程库 AI 主线 pandas 数据处理 › 第 5

清洗数据 —— 缺失值、重复值、类型转换

普通课60 分钟

本课导读

行业里有句实话:数据工作 80% 的时间在清洗。真实数据从不体面——有人没填的空格、导出两遍的重复行、混进文字的数字列、看不见的前后空格。本课学四套洗澡手法:查缺补缺(isna/dropna/fillna)、去重(drop_duplicates)、修类型(to_numeric)、洗字符串(str 系列)。这也是 U1.3 练习里"逐条 try、坏的隔离"的工业化版本。预计 60 分钟。

道具:一份典型的脏表

Python

体检报告解读:amount 列 Dtype 是 object(字符串!"缺货"把整列拖下水了——上上课的预言应验);name/city/amount 各有缺失;第 2 和第 4 行长得一模一样。开洗。

一、缺失值:查、删、补

Python

删还是补?没有标准答案,只有业务判断

  • 行数多、缺得零星 → dropna() 干脆
  • 缺的是关键列(金额)→ 补平均数/中位数会编造数据,慎重;有时"带着缺失算"(pandas 的统计函数默认跳过缺失)反而最诚实
  • 缺的是标签列(城市)→ 补个"未知"分类往往够用
  • 底线:无论删还是补,在分析报告里说明白你做了什么——清洗是透明的手术,不是掩盖

二、重复行:先看再删

Python

duplicated() 标记"和前面某行完全一样"的行;drop_duplicates() 删之。注意流程:先 duplicated 看清是什么在重复,确认是导出事故而非真实数据(同一人同额两笔真实消费!),再动手删——U1.3 的彩排规矩换了个舞台。

三、类型抢救:to_numeric

"缺货"混进数字列,整列沦为字符串,sum() 直接报错。抢救:

Python

errors="coerce" 是关键:转不动的不报错、不中断,统一变成缺失值 NaN——然后用第一节的框架统一处置。对比 U1.3 你手写的"逐条 try、坏的隔离",这就是它的一行工业版。(NaN 是 pandas 表示"缺失"的记号,describe/sum 都会自动跳过它。)

四、字符串清洗:str 系列

看不见的空格是最阴的脏——" 北京 ""北京" 是两个值,分组统计时北京会莫名分裂成两组:

Python

规律一点通:你会的字符串方法,加 str. 前缀就能整列用——df["col"].str.strip().str.lower().str.replace("旧", "新").str.contains("关键词")(这个返回布尔列,可直接当筛子)。U0.4 的字符串功夫全额继承。

顺带认识刚才偷跑上场的 value_counts()数一列里每个值各出现几次,按次数排好——它是"这列都有什么"的最快答案,也是查出"空格幽灵"这类分裂值的显微镜。

标准清洗流水线

四招连起来,就是拿到脏表的标准动作序列:

import pandas as pd

with open("dirty.csv", "w", encoding="utf-8") as f:    # 道具:一份微型脏表
    f.write("city,amount\n 上海,100\n上海,坏值\n北京,80\n北京,80")

df = pd.read_csv("dirty.csv")
df.info()                                              # ① 体检定位问题
df = df.drop_duplicates()                              # ② 去重(先 duplicated 确认)
df["amount"] = pd.to_numeric(df["amount"], errors="coerce")   # ③ 修类型
df["city"] = df["city"].str.strip()                    # ④ 洗字符串
print(df.isna().sum())                                 # ⑤ 盘点缺失 → 删或补
df = df.dropna(subset=["amount"])                      # (按业务决定,此处删关键列缺失)
print(df)

dropna(subset=["列"]):只有指定列缺失才删行——比无差别 dropna 更精准。)

✍️ 练习

练习 1:缺失值盘点与决策

对下表:① 打印每列缺失数 ② score 缺失的行删掉 ③ class 缺失的补"未分班",输出干净的表。

Python
✅ 参考答案
import pandas as pd

raw = """name,class,score
小雨,一班,92
阿杰,,88
丽丽,二班,
老王,一班,75"""
with open("cls.csv", "w", encoding="utf-8") as f:
    f.write(raw)
df = pd.read_csv("cls.csv")

print(df.isna().sum())
df = df.dropna(subset=["score"])          # 关键列缺失 → 删
df = df.fillna({"class": "未分班"})        # 标签列缺失 → 补
print(df)

同一张表两种策略并用——删还是补从来是按列决策,不是全表一刀切。

练习 2:拯救销量列

sales 列混进了脏值。用 to_numeric 抢救,报告:有效销量合计、被判为脏值的是哪些行。

Python
✅ 参考答案
import pandas as pd

raw = """shop,sales
东门店,1200
西门店,一千二
南门店,980
北门店,n/a
中心店,1500"""
with open("shops.csv", "w", encoding="utf-8") as f:
    f.write(raw)
df = pd.read_csv("shops.csv")

df["sales"] = pd.to_numeric(df["sales"], errors="coerce")
print(f'有效合计:{df["sales"].sum():.0f}')
print(df[df["sales"].isna()])

"一千二"这种人类友好、机器懵圈的值是真实报表常客——coerce 变 NaN 后亮出来交人工,别悄悄丢掉。

练习 3:全流程清洗(挑战)

这张表五毒俱全:重复行、城市带空格、amount 混脏值、缺失。跑一遍标准流水线,输出干净表 + 一句盘点(原几行 → 清后几行)。

Python
✅ 参考答案
import pandas as pd

raw = """name,city,amount
小雨, 上海,1200
阿杰,北京 ,800
阿杰,北京 ,800
丽丽,上海,离职折扣
老王, 深圳,950
小陈,北京,"""
with open("dirty_full.csv", "w", encoding="utf-8") as f:
    f.write(raw)
df = pd.read_csv("dirty_full.csv")
before = len(df)

df = df.drop_duplicates()
df["amount"] = pd.to_numeric(df["amount"], errors="coerce")
df["city"] = df["city"].str.strip()
df = df.dropna(subset=["amount"])

print(df)
print(f"清洗盘点:{before} 行 → {len(df)} 行")
print(df["city"].value_counts())   # 验证空格幽灵已消灭

📝 随堂测验

随堂测验0 / 5 题正确

1. df.isna().sum() 输出的是?

2. “删缺失还是补缺失”的正确决策方式是?

3. 删除重复行之前,专业的动作是?

4. pd.to_numeric(col, errors="coerce") 遇到转不动的值会?

5. 分组统计时“北京”莫名分成两组,最可能的原因和解法是?

本课小结

  • 标准流水线:体检(info)→ 去重(先看再删)→ 修类型(to_numeric+coerce)→ 洗字符串(str.strip)→ 处置缺失(按列删/补)
  • 删还是补是业务判断,处理过程在报告里交代——清洗是透明手术
  • errors="coerce":脏值统一变 NaN 再处置——"坏的隔离"的工业版
  • 新兵器 value_counts():一列的值分布,查分裂值/看类别构成的第一选择

数据洗净了,下一课就能问它真正的问题:"各城市卖了多少?哪类产品最赚?"——groupby,数据分析的核心动作登场。

划选正文任意文字可高亮、批注或加入复习卡

讨论

载入中…