重复数据处理
本教程共 54 篇 · 第 26 篇 · 更新于 2026-08-11 · 约 5 分钟阅读
本节目标:用 duplicated 找出重复行,用 drop_duplicates 删除重复,理解 keep 和 subset 参数,并会处理索引标签重复。
重复数据从哪来
导入多次、程序重复写入、多表合并后叠加——重复行在真实数据里太常见了。不处理的话,sum、mean、count 全部失真。
duplicated:标记重复
duplicated 返回布尔 Series,True 表示”这一行在前面出现过”:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "张三", "王五", "李四"],
"年龄": [25, 30, 25, 28, 30],
})
print(df.duplicated())
print(df.duplicated().sum()) # 2 行重复
第一行”张三 25”是首次出现,标 False;第三行和它完全一样,标 True。
keep 参数决定”谁算重复”:
- keep=“first”(默认):第一次出现保留,后面的标 True
- keep=“last”:最后一次保留,前面的标 True
- keep=False:所有重复行(包括第一次)都标 True
print(df.duplicated(keep="last"))
print(df.duplicated(keep=False))
subset 指定只看某些列,比如”姓名相同就算重复”:
print(df.duplicated(subset=["姓名"]))
Note判断重复比较的是值本身。注意带 NaN 的行:duplicated 把 NaN 视为彼此相等(与
==的语义不同),所以含 NaN 的行也会被判重、被去重。
drop_duplicates:删除重复
drop_duplicates 直接返回去重后的表,参数和 duplicated 一一对应:
print(df.drop_duplicates())
print(df.drop_duplicates(keep="last"))
print(df.drop_duplicates(keep=False))
print(df.drop_duplicates(subset=["姓名"]))
去重后行号会留下空洞,加 ignore_index=True 恢复连续索引:
print(df.drop_duplicates(ignore_index=True))
Tip去重后复查:
df.duplicated().sum()应变成 0。去重”删行”,配合 dropna(§24)一起构成清洗的第一步。
先定义什么叫重复
“重复”不是数据自带的属性,是你定义的:
- 完全重复:所有列都相同,drop_duplicates() 不传 subset 即可
- 部分重复:只看关键列(比如订单号),其他列允许不同,用 subset 指定
subset 选哪些列,取决于业务上”什么相同就算同一条”。选宽了会误删,选窄了漏删,先想清楚再动手。
场景一:完全重复的行
整行一模一样才删,直接 drop_duplicates() 不传参数。最常见于”同一份数据导入了两遍”。删除前可以先用 value_counts 看看哪几行重复最多:
print(df.value_counts().head())
场景二:按业务键去重,保留最新
同一订单号出现多次,想留日期最新的那条。先按日期排序,再按订单号去重并保留”最后一条”:
orders = pd.DataFrame({
"订单号": ["O001", "O002", "O001", "O003"],
"日期": ["2024-01-01", "2024-01-02", "2024-01-05", "2024-01-04"],
"金额": [100, 200, 150, 300],
})
print(orders.sort_values("日期").drop_duplicates(subset=["订单号"], keep="last"))
O001 出现两次,留下 1 月 5 日那条。
场景三:每组保留最大值
比如每个客户保留消费最高的记录:先按消费降序排序,再按客户去重(每组留第一条):
sales = pd.DataFrame({
"客户": ["A", "A", "B", "B"],
"月份": ["01", "02", "01", "02"],
"消费": [100, 200, 300, 250],
})
print(sales.sort_values("消费", ascending=False).drop_duplicates(subset=["客户"]))
先排序、再去重、keep 定取舍——这个组合拳能解决大部分”每组挑一条”的需求。
场景四:多列组合键
单列判断不够时,subset 传多个列名,组合相同才算重复。比如”客户 + 产品”重复才是真正的重复订单:
df2 = pd.DataFrame({
"客户": ["A", "A", "A", "B"],
"产品": ["手机", "手机", "电脑", "手机"],
"金额": [5000, 5000, 8000, 6000],
})
print(df2.drop_duplicates(subset=["客户", "产品"]))
去重前后的统计对比
去重为什么重要,看一个数字就够了。同一份订单被算了两次,总金额直接虚高:
print("去重前总金额:", orders["金额"].sum())
orders_clean = orders.sort_values("日期").drop_duplicates(subset=["订单号"], keep="last")
print("去重后总金额:", orders_clean["金额"].sum())
先看数字差异,再决定要不要删——差异大说明重复确实在污染统计结果。
只标记不删除
有些场景不想删,只想留个标记(比如导出前人工复核)。duplicated 的结果可以直接存成一列:
df3 = df.copy()
df3["是否重复"] = df3.duplicated(keep=False)
print(df3)
索引标签重复
行数据不重复,但索引标签重复(比如两行都叫 “b”),同样会捣乱:loc[“b”] 返回两行,reindex 直接报错(§23)。
s = pd.Series([0, 1, 2], index=["a", "b", "b"])
print(s.index.is_unique) # False
print(s.index.duplicated()) # [False False True]
print(s.loc[~s.index.duplicated()]) # 保留第一条
或者把同标签的行合并:s.groupby(level=0).mean()。想从源头禁止重复,用 set_flags(allows_duplicate_labels=False)(§23 有完整例子)。
Warning去重前先搞清楚重复的原因。同一订单号对应不同金额时,直接删可能删掉正确的那条。先
df[df.duplicated(subset=["订单号"], keep=False)]把重复的全部捞出来看一遍,再决定保留策略。
小结
duplicated 标记、drop_duplicates 删除,keep 决定留哪条,subset 定义”什么算重复”。先对比去重前后的统计数字,再决定删不删——重复不一定是脏数据,可能是”重复”的粒度没定义对。