duplicated() 返回布尔序列而非布尔索引,因其仅标识每行是否在之前已出现,默认首次为False、后续同值为True;支持keep参数控制标记逻辑,subset指定列去重,NaN被默认视为相等,需预处理以匹配业务语义。

为什么 duplicated() 返回的不是布尔索引而是布尔序列
duplicated() 本身不修改原数据,只返回一个 Series,每个元素对应行是否“在它之前已出现过”。默认情况下,它把第一次出现的记录视为“非重复”,后续相同行才标为 True。这点容易误解——你以为它标记所有重复项,其实它标记的是“重复发生的位置”。
常见错误现象:df[df.duplicated()] 只能拿到重复行(不含首次),而 df[~df.duplicated()] 才是去重后保留首行的结果。
- 用
keep='first'(默认):首次出现为False,后续同值行为True - 用
keep='last':最后一次出现为False,其余同值行为True - 用
keep=False:所有重复值(含首次和末次)都标为True,适合“全删不留”场景
按指定列判断重复,而不是整行
实际数据中,往往只需关注某些字段是否重复,比如只看 'email' 或 ['user_id', 'date'] 组合。这时必须显式传入 subset 参数,否则默认检查所有列,小数精度、空值或字符串前后空格差异都会导致误判。
示例:df.duplicated(subset=['name', 'age']) 表示只要这两列组合一致,就算重复;而 df.duplicated(subset='email') 等价于 df.duplicated(subset=['email'])。
立即学习“Python免费学习笔记(深入)”;
-
subset必须是列表或单个字符串,不能是元组或None - 若某行在
subset列中有NaN,Pandas 默认把它们视为“相等”,即NaN == NaN成立(这是 Pandas 的设计,不同于 Python 原生逻辑) - 如需排除空值干扰,先用
dropna(subset=[...])或手动填充再查重
drop_duplicates() 和 duplicated().index 删除方式的区别
直接调用 df.drop_duplicates() 最简洁,但它内部就是基于 duplicated() 实现的。两者关键差异在于可控性:
-
df.drop_duplicates(keep='last', inplace=True):一步到位,但无法单独捕获被删行做日志或备份 -
dup_mask = df.duplicated(keep=False)→dup_rows = df[dup_mask]→ 再df = df[~dup_mask]:可先检查dup_rows内容,确认无误后再删 - 性能上,
drop_duplicates()更快;调试时,分步用duplicated()更安全
注意:inplace=True 在新版 Pandas 中已不推荐,优先用赋值写法 df = df.drop_duplicates(...),避免链式赋值警告。
空值(NaN)、类型混杂与排序影响
duplicated() 对 NaN 的处理是隐式且不可关闭的:所有 NaN 在比较中互认相等。这在清洗用户注册表时很危险——比如 phone 列大量为空,会被当成同一重复组。
另外,如果列含混合类型(如 int 和 str 数字混存),或未标准化('123' vs 123),duplicated() 会严格按值+类型判定,结果可能不符合业务预期。
- 建议预处理:用
df[col].fillna('MISSING')替换空值,或用df[col].astype(str)统一类型 - 若需按“逻辑重复”而非“字面重复”判断(如忽略大小写、空格),得先
df['email'] = df['email'].str.strip().str.lower()再查重 - 结果顺序取决于原始行序,
duplicated()不改变顺序;但若你先sort_values()再查重,重复判定逻辑会变(因为“之前出现”参考系变了)
真正麻烦的不是语法,而是搞清“你定义的重复到底是什么”——业务规则永远比代码逻辑更难对齐。


















