别再只会用drop_duplicates了!Pandas数据去重,这8个隐藏技巧帮你搞定90%的脏数据
·
Pandas数据去重实战:8个高阶技巧解决90%业务场景
数据清洗是数据分析过程中最耗时却又最关键的环节。在实际业务中,我们遇到的重复数据远比教科书中的示例复杂——可能是跨多列的复合重复,需要根据时间戳保留最新记录,或是按特定业务规则筛选唯一值。本文将带你突破drop_duplicates()的基础用法,掌握一套应对真实场景的系统化解决方案。
1. 理解重复数据的本质分类
在开始技术操作前,我们需要建立对重复数据的类型学认知。根据业务场景不同,重复数据至少可分为三种典型情况:
- 完全重复:所有列值完全相同的记录(可用
df.duplicated()检测) - 关键字段重复:业务主键相同但其他字段不同的记录(如用户ID相同但行为数据不同)
- 逻辑重复:业务含义相同但表现形式不同的记录(如"iPhone13"和"苹果手机13")
# 完全重复检测示例
import pandas as pd
data = {
'order_id': [1001, 1002, 1001, 1004],
'product': ['A', 'B', 'A', 'C'],
'price': [299, 399, 299, 199]
}
df = pd.DataFrame(data)
print(df[df.duplicated(keep=False)]) # keep=False标记所有重复项
输出结果将显示order_id为1001的完全重复记录。但现实情况往往更复杂:
| 重复类型 | 检测方法 | 处理难点 |
|---|---|---|
| 完全重复 | duplicated() |
容易处理,但可能误伤 |
| 关键字段重复 | subset参数 |
需要确定业务主键 |
| 逻辑重复 | 模糊匹配 | 需要自定义规则 |
2. 多列组合去重的进阶策略
当需要基于多列组合判断重复时,subset参数只是起点。更专业的做法是:
# 电商订单去重案例:同一用户同一天购买同一商品算重复
ecommerce_data = {
'user_id': [101, 101, 102, 103, 101],
'product_id': [2001, 2001, 2003, 2004, 2001],
'order_date': ['2023-01-01', '2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03'],
'quantity': [1, 2, 1, 3, 1]
}
df = pd.DataFrame(ecommerce_data)
# 方法1:标准去重(保留第一条)
basic_dedupe = df.drop_duplicates(subset=['user_id', 'product_id', 'order_date'])
# 方法2:自定义保留逻辑(保留购买数量最大的记录)
advanced_dedupe = df.sort_values('quantity', ascending=False)\
.drop_duplicates(subset=['user_id', 'product_id', 'order_date'])
提示:当处理时间序列数据时,建议先用
sort_values按时间排序,再配合keep='last'保留最新记录
3. 阈值控制与条件去重
某些业务场景需要保留出现一定次数的重复项,这时就需要引入阈值控制:
# 用户行为日志分析:只保留访问超过3次的记录
user_logs = {
'user_id': [101, 101, 101, 102, 102, 103, 104, 104, 104, 104],
'page_url': ['/home', '/product', '/cart', '/home', '/product', '/home', '/login', '/profile', '/settings', '/logout']
}
df = pd.DataFrame(user_logs)
# 计算每用户出现次数
df['count'] = df.groupby('user_id')['user_id'].transform('count')
# 阈值过滤
threshold = 3
filtered_df = df[df['count'] >= threshold].drop(columns=['count'])
更优雅的实现方式是使用groupby+filter:
filtered_df = df.groupby('user_id').filter(lambda x: len(x) >= threshold)
4. 处理近似重复数据
真实数据中常存在非精确重复的情况,如:
- 同一商品不同写法("MacBook Pro 13''" vs "13-inch MacBook Pro")
- 地址缩写差异("St." vs "Street")
- 数值四舍五入差异(19.99 vs 20.0)
这时需要模糊匹配技术:
from fuzzywuzzy import fuzz
products = ['iPhone 13', 'iphone13', 'Apple iPhone 13', 'Samsung Galaxy']
# 构建相似度矩阵
similarity_matrix = [[fuzz.ratio(p1, p2) for p1 in products] for p2 in products]
# 转换为DataFrame
sim_df = pd.DataFrame(similarity_matrix, index=products, columns=products)
print(sim_df)
输出结果将显示各产品名称间的相似度百分比,可设定阈值(如>80%)判定为重复。
5. 分组去重与聚合运算
有时我们需要在去重前后进行聚合计算:
# 销售数据:保留每个产品的最高销售额记录
sales_data = {
'product': ['A', 'A', 'B', 'B', 'C'],
'sales': [100, 150, 200, 180, 90],
'date': ['2023-01-01', '2023-01-15', '2023-01-10', '2023-01-20', '2023-01-05']
}
df = pd.DataFrame(sales_data)
# 方法1:先排序再去重
df.sort_values('sales', ascending=False).drop_duplicates('product')
# 方法2:使用groupby+idxmax
max_indices = df.groupby('product')['sales'].idxmax()
df.loc[max_indices]
两种方法对比:
| 方法 | 优点 | 缺点 |
|---|---|---|
| 排序去重 | 代码简洁 | 需要全表排序 |
| groupby+idxmax | 性能更好 | 需要额外索引操作 |
6. 处理时间序列重复数据
时间序列数据的去重需要特殊处理:
# 传感器数据清洗:保留每个时间点的最新读数
sensor_data = {
'timestamp': ['2023-01-01 08:00:00', '2023-01-01 08:00:05',
'2023-01-01 08:00:05', '2023-01-01 08:00:10'],
'sensor_id': [1, 1, 1, 2],
'value': [23.5, 23.7, 23.6, 42.1]
}
df = pd.DataFrame(sensor_data)
df['timestamp'] = pd.to_datetime(df['timestamp'])
# 关键步骤:先按时间倒序排序
df = df.sort_values('timestamp', ascending=False)
# 保留每个传感器在每个时间点的第一条记录
clean_df = df.drop_duplicates(['sensor_id', 'timestamp'])
注意:处理时间数据时务必先转换为datetime类型,否则字符串比较可能导致意外结果
7. 内存优化型去重技巧
处理大型数据集时,标准去重方法可能内存不足。这时可采用:
分块处理法:
chunk_size = 100000
unique_records = pd.DataFrame()
for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size):
chunk = chunk.drop_duplicates(subset=['key_column'])
unique_records = pd.concat([unique_records, chunk])
unique_records = unique_records.drop_duplicates(subset=['key_column'])
哈希去重法:
# 为每行生成唯一哈希值
df['hash'] = df.apply(lambda row: hash(tuple(row)), axis=1)
# 基于哈希值去重
df = df.drop_duplicates('hash').drop(columns=['hash'])
8. 验证去重结果的完整性
去重后必须验证业务逻辑完整性:
# 验证示例:确保关键业务ID仍保持唯一
original_count = df['customer_id'].nunique()
deduped_count = clean_df['customer_id'].nunique()
assert original_count == deduped_count, "去重操作导致客户ID丢失!"
# 验证数据完整性
validation_results = {
'指标': ['原始行数', '去重后行数', '重复行数', '唯一值比例'],
'值': [len(df), len(clean_df), len(df)-len(clean_df),
round(len(clean_df)/len(df)*100, 2)]
}
pd.DataFrame(validation_results)
在实际项目中,我通常会建立一套完整的验证流程:
- 业务主键唯一性检查
- 关键指标统计值对比(如总和、平均值)
- 时间范围一致性验证
- 抽样检查典型重复案例
掌握这些技巧后,面对各种复杂的业务数据,你都能游刃有余地处理重复问题。记住,好的数据清洗不是简单地删除重复行,而是根据业务需求智能地保留最有价值的信息。
更多推荐


所有评论(0)