课程库 › AI 主线 › pandas 数据处理 › 第 5 课
清洗数据 —— 缺失值、重复值、类型转换
普通课约 60 分钟
本课导读
行业里有句实话:数据工作 80% 的时间在清洗。真实数据从不体面——有人没填的空格、导出两遍的重复行、混进文字的数字列、看不见的前后空格。本课学四套洗澡手法:查缺补缺(isna/dropna/fillna)、去重(drop_duplicates)、修类型(to_numeric)、洗字符串(str 系列)。这也是 U1.3 练习里"逐条 try、坏的隔离"的工业化版本。预计 60 分钟。
道具:一份典型的脏表
体检报告解读:amount 列 Dtype 是 object(字符串!"缺货"把整列拖下水了——上上课的预言应验);name/city/amount 各有缺失;第 2 和第 4 行长得一模一样。开洗。
一、缺失值:查、删、补
删还是补?没有标准答案,只有业务判断:
- 行数多、缺得零星 →
dropna()干脆 - 缺的是关键列(金额)→ 补平均数/中位数会编造数据,慎重;有时"带着缺失算"(pandas 的统计函数默认跳过缺失)反而最诚实
- 缺的是标签列(城市)→ 补个"未知"分类往往够用
- 底线:无论删还是补,在分析报告里说明白你做了什么——清洗是透明的手术,不是掩盖
二、重复行:先看再删
duplicated() 标记"和前面某行完全一样"的行;drop_duplicates() 删之。注意流程:先 duplicated 看清是什么在重复,确认是导出事故而非真实数据(同一人同额两笔真实消费!),再动手删——U1.3 的彩排规矩换了个舞台。
三、类型抢救:to_numeric
"缺货"混进数字列,整列沦为字符串,sum() 直接报错。抢救:
errors="coerce" 是关键:转不动的不报错、不中断,统一变成缺失值 NaN——然后用第一节的框架统一处置。对比 U1.3 你手写的"逐条 try、坏的隔离",这就是它的一行工业版。(NaN 是 pandas 表示"缺失"的记号,describe/sum 都会自动跳过它。)
四、字符串清洗:str 系列
看不见的空格是最阴的脏——" 北京 " 和 "北京" 是两个值,分组统计时北京会莫名分裂成两组:
规律一点通:你会的字符串方法,加 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 缺失的补"未分班",输出干净的表。
✅ 参考答案
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 抢救,报告:有效销量合计、被判为脏值的是哪些行。
✅ 参考答案
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 混脏值、缺失。跑一遍标准流水线,输出干净表 + 一句盘点(原几行 → 清后几行)。
✅ 参考答案
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()) # 验证空格幽灵已消灭
📝 随堂测验
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,数据分析的核心动作登场。
划选正文任意文字可高亮、批注或加入复习卡
讨论
载入中…