模块三-数据清洗与预处理——14. 重复值处理
14. 重复值处理
1. 概述
重复值是数据中的常见问题,可能来自数据录入错误、系统重复导出、数据合并等原因。重复数据会导致统计偏差、模型过拟合,需要在数据预处理阶段处理。
importpandasaspdimportnumpyasnp# 创建包含重复值的示例数据df=pd.DataFrame({'ID':[1,2,3,4,5,6,7,8,9,10],'姓名':['张三','李四','王五','张三','赵六','李四','钱七','张三','孙八','李四'],'年龄':[25,30,28,25,32,30,35,25,27,30],'城市':['北京','上海','广州','北京','深圳','上海','杭州','北京','成都','上海'],'工资':[8000,12000,10000,8000,15000,12000,11000,8000,9500,12000]})print("原始数据:")print(df)2. 检测重复值
2.1 duplicated() 方法
duplicated()返回布尔 Series,True 表示重复(除第一次出现外)。
# 检测完全重复的行print("完全重复检测:")print(df.duplicated())# 查看重复行print("\n重复的行:")print(df[df.duplicated()])# 保留最后一次出现print("\n保留最后一次出现(标记第一次为重复):")print(df[df.duplicated(keep='last')])# 标记所有重复(包括第一次)print("\n标记所有重复:")print(df[df.duplicated(keep=False)])2.2 指定列检测
# 基于指定列检测重复print("基于'姓名'列检测重复:")print(df.duplicated(subset=['姓名']))# 查看姓名重复的行print("\n姓名重复的行:")print(df[df.duplicated(subset=['姓名'])])# 基于多列检测print("\n基于'姓名'和'年龄'检测重复:")print(df.duplicated(subset=['姓名','年龄']))3. 删除重复值
3.1 drop_duplicates() 基础
# 删除完全重复的行(保留第一次出现)print("删除完全重复:")df_dedupe=df.drop_duplicates()print(df_dedupe)print(f"删除前:{len(df)}行, 删除后:{len(df_dedupe)}行")# 保留最后一次出现print("\n保留最后一次出现:")print(df.drop_duplicates(keep='last'))# 删除所有重复(包括第一次)print("\n删除所有重复(只保留唯一值):")print(df.drop_duplicates(keep=False))3.2 指定列删除
# 基于姓名列去重print("基于姓名去重:")print(df.drop_duplicates(subset=['姓名']))# 基于多列去重print("\n基于姓名和年龄去重:")print(df.drop_duplicates(subset=['姓名','年龄']))# 原地修改df_copy=df.copy()df_copy.drop_duplicates(inplace=True)print("\n原地修改后:")print(df_copy)4. 重复值处理策略
4.1 聚合重复值
# 对重复值进行聚合(求和、均值等)print("按姓名聚合工资:")df_agg=df.groupby('姓名').agg({'年龄':'first','城市':'first','工资':'sum'}).reset_index()print(df_agg)# 保留最大值print("\n保留每个姓名的最大工资:")df_max=df.sort_values('工资',ascending=False).drop_duplicates(subset=['姓名'])print(df_max)# 保留最新记录(假设有日期列)# df_sorted = df.sort_values('日期', ascending=False).drop_duplicates(subset=['姓名'])4.2 标记重复值
# 添加重复标记列df['是否重复']=df.duplicated(keep=False)print("添加重复标记:")print(df)# 添加重复次数print("\n添加重复次数:")df['重复次数']=df.groupby('姓名')['姓名'].transform('size')print(df)5. 重复值统计
# 统计完全重复行数print(f"完全重复行数:{df.duplicated().sum()}")# 统计各列的重复情况forcolindf.columns:dup_count=df.duplicated(subset=[col]).sum()print(f"{col}列重复行数:{dup_count}")# 查看重复值的分布print("\n姓名重复次数:")print(df['姓名'].value_counts())# 查看重复行详细信息print("\n重复行详情:")dup_rows=df[df.duplicated(keep=False)].sort_values('姓名')print(dup_rows)6. 完整示例:用户数据去重
# 创建用户数据np.random.seed(42)users=pd.DataFrame({'user_id':[1001,1002,1003,1001,1004,1002,1005,1001,1006,1002],'姓名':['张三','李四','王五','张三','赵六','李四','钱七','张三','孙八','李四'],'年龄':[25,30,28,25,32,30,35,25,27,30],'城市':['北京','上海','广州','北京','深圳','上海','杭州','北京','成都','上海'],'注册时间':pd.date_range('2024-01-01',periods=10,freq='D'),'消费金额':np.random.randint(100,1000,10)})print("="*60)print("用户数据去重处理")print("="*60)print("\n原始数据:")print(users)print("\n1. 重复检测:")print(f"完全重复行数:{users.duplicated().sum()}")print(f"user_id 重复行数:{users.duplicated(subset=['user_id']).sum()}")print(f"姓名重复行数:{users.duplicated(subset=['姓名']).sum()}")# 2. 查看重复用户print("\n2. 重复的用户:")dup_users=users[users.duplicated(subset=['user_id'],keep=False)].sort_values('user_id')print(dup_users)# 3. 按 user_id 去重(保留最新注册的)print("\n3. 按 user_id 去重(保留最新注册):")users_unique=users.sort_values('注册时间',ascending=False).drop_duplicates(subset=['user_id'])print(users_unique)# 4. 聚合重复用户数据print("\n4. 聚合重复用户:")users_agg=users.groupby('user_id').agg({'姓名':'first','年龄':'first','城市':'first','注册时间':'max','消费金额':'sum'}).reset_index()print(users_agg)# 5. 最终数据print("\n5. 最终去重后数据:")print(f"去重前:{len(users)}行")print(f"去重后:{len(users_unique)}行")7. 重复值处理决策流程
发现重复值 │ ├─ 完全重复(所有列相同) │ │ │ └─ 直接删除,保留一条 │ ├─ 部分列重复(如 ID 相同但其他列不同) │ │ │ ├─ 需要合并信息 → 聚合(求和、均值、拼接) │ ├─ 需要保留最新 → 按时间排序后去重 │ ├─ 需要保留最完整 → 按完整度排序后去重 │ └─ 无法确定 → 标记后人工处理 │ └─ 业务逻辑决定 │ ├─ 用户 ID 必须唯一 → 基于 ID 去重 ├─ 订单号必须唯一 → 基于订单号去重 └─ 允许重复 → 保留8. 常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 大小写敏感 | ‘张三’ 和 '张三 ’ 被视为不同 | 先用str.strip()和str.lower()标准化 |
| 浮点数精度 | 1.0 和 1.0000001 被视为不同 | 使用round()或np.isclose() |
| 缺失值 | NaN 与 NaN 不被视为重复 | 先处理缺失值再去重 |
| 索引影响 | 索引不同但数据相同也会去重 | 使用ignore_index=True |
# 处理大小写和空格print("\n处理大小写和空格:")df_name=pd.DataFrame({'姓名':['张三','张三 ',' 张三','李四','李四']})df_name['姓名_clean']=df_name['姓名'].str.strip().str.lower()print(df_name)print(f"清洗后重复:{df_name['姓名_clean'].duplicated().sum()}")9. 总结
| 方法 | 用途 | 示例 |
|---|---|---|
duplicated() | 检测重复 | df.duplicated() |
duplicated(keep='last') | 标记最后出现为重复 | df.duplicated(keep='last') |
duplicated(keep=False) | 标记所有重复 | df.duplicated(keep=False) |
duplicated(subset=[...]) | 基于指定列检测 | df.duplicated(subset=['col1','col2']) |
drop_duplicates() | 删除重复 | df.drop_duplicates() |
drop_duplicates(keep='last') | 保留最后出现 | df.drop_duplicates(keep='last') |
drop_duplicates(keep=False) | 删除所有重复 | df.drop_duplicates(keep=False) |
drop_duplicates(subset=[...]) | 基于指定列删除 | df.drop_duplicates(subset=['col1']) |
groupby().agg() | 聚合重复值 | df.groupby('key').agg({'value':'sum'}) |
