duplicated()返回布尔Series标记重复行(首次为False),drop_duplicates()用于去重;groupby().agg()实现多维合并聚合,需注意NaN处理和索引保留。

用 duplicated() 和 drop_duplicates() 快速定位重复行
直接调用 df.duplicated() 是最轻量的判断方式,它返回布尔 Series,True 表示该行在它之前已出现过。注意默认只检查**全部列**,且首次出现标记为 False,后续重复才为 True。
常见错误是误以为它能“标出所有重复组”,其实它只做顺序标记;想看每组重复行的完整集合,得配合布尔索引:df[df.duplicated(keep=False)] —— 这里 keep=False 让所有重复行都标为 True。
- 若只关心某几列是否重复,传入
subset=['col_a', 'col_b'] -
keep='first'(默认):保留首次出现的行;keep='last':保留最后一次;keep=False:全标为重复 - 性能上,
duplicated()比groupby().size()快得多,尤其对百万级数据
用 groupby().agg() 实现多维合并(非简单去重)
“多维合并”通常不是删掉重复,而是按多列分组后,对其他列做聚合(比如把相同 ID 的多个地址拼成列表,或取最新时间戳)。这时不能只靠 drop_duplicates(),得用 groupby() + 自定义聚合逻辑。
例如:按 ['user_id', 'product_id'] 分组,合并 tags 列为去重列表,取 updated_at 最大值:
立即学习“Python免费学习笔记(深入)”;
df.groupby(['user_id', 'product_id']).agg({
'tags': lambda x: list(set(x)),
'updated_at': 'max'
}).reset_index()
- 避免直接用
list聚合字符串列——会保留重复项;改用lambda x: ';'.join(x.unique())更可控 - 如果某列需保留首条非空值,用
'first'或lambda x: x.dropna().iloc[0] if not x.dropna().empty else None -
agg()里混用字符串简写(如'max')和函数(如lambda)是安全的,Pandas 会自动分发
合并时保留原始索引或添加计数列
很多场景需要知道“这组重复数据原来有几条”,或者“原始位置在哪”。duplicated() 不提供计数,但 groupby().size() 可以;而索引信息容易在 groupby().agg() 中丢失。
- 加计数列:
df.groupby(['a','b']).size().rename('count').reset_index() - 保留原始索引(如取每组第一条的完整行):
df.loc[df.groupby(['a','b']).apply(lambda x: x.index[0])] - 更稳妥的做法是先
df.assign(orig_idx=df.index),再 groupby,这样索引变成普通列,不会丢
注意 NaN 在重复判断和分组中的特殊行为
NaN != NaN,所以默认情况下,含 NaN 的行不会被 duplicated() 当作重复,groupby() 也会把 NaN 单独分到一组——这常导致漏判或意外分组。
- 想把
NaN视为相同值来去重,先用df.fillna(method='ffill').fillna(method='bfill')做临时填充(慎用),或改用df.replace({np.nan: None}).duplicated()(None 在 groupby 中会被统一归为一组) - 检查
NaN分布:df.isna().sum()和df.groupby(df.isna().sum(axis=1) > 0).size()能快速看出空值模式 - 用
pd.concat([df, df.fillna(-999)], ignore_index=True)模拟 NaN 合并逻辑前,务必确认 -999 不是业务合法值
多维合并真正麻烦的从来不是语法,而是搞清“哪些列定义‘重复’、哪些列需要怎么聚合、NaN 算不算相同、原始顺序要不要保”——这几个问题没理清,代码写得再短也没用。


















