drop_duplicates() 默认保留第一次出现的行,按原始顺序从上往下扫描并删除后续重复行;keep='last'保留最后一次出现的行,keep=False则删除所有重复行。

drop_duplicates() 默认行为到底删哪一行
不指定 keep 参数时,drop_duplicates() 会保留**第一次出现的行**,把后面所有重复的都删掉。这不是“留最新”也不是“留最后”,就是按原始顺序从上往下扫,遇到重复就扔后面的。
常见错误现象:df.drop_duplicates(subset=['user_id']) 后发现用户最新一条记录没了——因为数据是按时间倒序排的,第一条反而是最老的记录。
- 想留最新(比如按时间排序后最后一版),先用
sort_values()把关键列(如'updated_at')升序或降序排好,再调drop_duplicates() -
keep='first'和默认行为一样;keep='last'留最后一次出现的;keep=False则把所有重复行全删光(一个都不留) - 如果
subset指定多列,比如['user_id', 'product_id'],那只有这两列值完全一致才算重复
subset 里混了 NaN 怎么办
NaN 在 pandas 里不等于自己,所以 drop_duplicates(subset=['col_a']) 遇到多个 NaN 时,默认会把它们全当成“不同值”,结果就是这些带 NaN 的行全被保留下来——看起来像没去重。
使用场景:清洗用户表,'phone' 列大量为空,你本意是“手机号相同的只留一条”,但空值全留下来了,数量没变。
- 加参数
keep='first'不解决 NaN 比较问题,得先处理缺失值 - 稳妥做法是提前填充或标记:比如
df['phone'].fillna('MISSING')再去重 - 或者用
df.drop_duplicates(subset=['col_a'], ignore_index=True)并不管 NaN,但逻辑已偏离原意——别依赖这个“绕过”
性能差?可能是 subset 列没建索引或类型太杂
drop_duplicates() 内部靠哈希比对,列数据类型和是否含 object 类型影响很大。比如 subset=['user_name'] 是字符串列,且长度差异大、内容随机,哈希开销就高;而 ['user_id'] 是 int64,快得多。
参数差异:subset 越长、越宽(尤其含长文本、嵌套 dict/list),内存占用和耗时增长越明显。
- 确认
subset列的数据类型:用df.dtypes看,把能转成 category 或 int 的先转了 - 避免用
subset=['col_a', 'col_b', 'col_c', ...]拉太多列,只放真正决定“重复”的最小字段集 - 大数据量(千万行以上)时,考虑先
df.sort_values(subset).drop_duplicates(subset, keep='last'),有时比直接去重更快(利用排序局部性)
inplace=True 是不是更省内存
不是。pandas 0.25+ 版本起,inplace=True 已被标记为弃用,且它并不真正节省内存——底层仍是生成新对象再赋值,只是语法上省了个 =。
容易踩的坑:写 df.drop_duplicates(inplace=True) 后接着链式调用,比如 .reset_index(),会报错,因为返回的是 None。
- 统一用
df = df.drop_duplicates(...),语义清晰,调试也方便 - 如果真卡内存,优先检查
subset是否合理、数据类型是否可压缩,而不是迷信inplace - 注意
drop_duplicates()返回的是视图还是副本?答案是:总是新 DataFrame(除非空操作),原df不变
最常被忽略的一点:去重逻辑是否该由 pandas 承担?比如业务上“相同 user_id + 相同 order_time 视为同一单”,但时间字段带毫秒、数据库写入有延迟,直接按原值去重可能漏掉本该合并的行——这时候得先做时间对齐(如截断到秒级),再 drop_duplicates。


















