影刀RPA 数据去重的七种方法:从简单到复杂的选型

0 阅读6分钟

影刀RPA 数据去重的七种方法:从简单到复杂的选型

作者:林焱

什么情况用这个

你从三个平台采集了商品数据,合并后发现有重复。Excel里的订单记录导入了两次,客户名单里有同名同姓不同人、也有同一人多条记录。 在这里插入图片描述

数据去重听起来简单——不就 drop_duplicates() 嘛?实际远不止。不同场景需要不同的去重策略:有时候全字段相同才是重复,有时候只要ID相同就算重复;有时候要保留第一条,有时候要合并两条记录的信息。这篇文章从简单到复杂,把七种去重方法的使用场景和选择逻辑讲清楚。

怎么做

方法一:集合去重(最简单)

适合:单列数据、全字段重复的场景。

# 字符串列表去重
names = ["张三", "李四", "张三", "王五", "李四"]
unique_names = list(set(names))
print(unique_names)  # ['张三', '李四', '王五'](顺序不保证)

# 保持原顺序的去重
seen = set()
unique_ordered = []
for name in names:
    if name not in seen:
        seen.add(name)
        unique_ordered.append(name)
print(unique_ordered)  # ['张三', '李四', '王五']

方法二:字典键去重(按关键字段)

在这里插入图片描述

适合:多个字段中只有某个字段决定是否重复(如ID)。

records = [
    {"id": "001", "name": "商品A", "price": 99},
    {"id": "002", "name": "商品B", "price": 199},
    {"id": "001", "name": "商品A(新)", "price": 109},  # 同ID,信息更新了
]

# 按ID去重,保留第一次出现的
seen = {}
for record in records:
    key = record["id"]
    
[video(video-owL94VaT-1784132827886)(type-csdn)(url-https://live.csdn.net/v/embed/525010)(image-https://v-blog.csdnimg.cn/asset/f4faa587144cb7070f19e8b36813806b/cover/Cover0.jpg)(title-店群矩阵自动化突破运营极限!)]

    if key not in seen:
        seen[key] = record

unique_records = list(seen.values())

# 如果需要保留最后出现的(更新的数据)
for record in records:
    seen[record["id"]] = record  # 直接覆盖

方法三:多字段组合键去重

适合:需要多个字段组合才能判断重复。

# 按"姓名+手机号"组合去重
contacts = [
    {"name": "张三", "phone": "13800138000", "email": "a@x.com"},
    {"name": "张三", "phone": "13900139000", "email": "b@x.com"},  # 同名人不同手机
    {"name": "张三", "phone": "13800138000", "email": "c@x.com"},  # 重复(同名同手机)
]

def dedup_by_keys(records, keys):
    """按多个字段组合去重"""
    seen = set()
    result = []
    
    for record in records:
    ![在这里插入图片描述](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f4d9a8daf68e4334a8387b6b875259fd~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5p6X54SxUlBB:q75.awebp?rk3s=f64ab15b&x-expires=1786209367&x-signature=6G%2FqUuKZnXuI%2B7jB1FHvgb9A978%3D)

        # 生成组合键
        composite_key = tuple(record.get(k, "") for k in keys)
        
        if composite_key not in seen:
            seen.add(composite_key)
            result.append(record)
    
    return result

unique = dedup_by_keys(contacts, ["name", "phone"])
# 结果:张三+138... 和 张三+139... 两条

方法四:模糊匹配去重

适合:文本不完全一样但基本是同一内容的场景。

import difflib

def fuzzy_dedup(strings, threshold=0.85):
    """模糊去重——相似度超过阈值的视为重复"""
    unique = []
    
    for s in strings:
        is_dup = False
        for existing in unique:
            similarity = difflib.SequenceMatcher(None, s, existing).ratio()
            if similarity >= threshold:
                is_dup = True
                break
        
        if not is_dup:
            unique.append(s)
    
    return unique

# 示例:公司名稍有不同的记录
names = [
    "深圳市腾讯计算机系统有限公司",
    "深圳腾讯计算机系统有限公司",  # 少了"市"
    "深圳市腾讯科技公司",           # 不同
]

unique_names = fuzzy_dedup(names, threshold=0.8)
print(unique_names)  # 前两条会去重

注意:模糊匹配去重计算量大,数据量>1000条时建议先做精确去重缩小范围。

方法五:合并去重(取并集)

适合:两条重复记录互补的信息要合并。

在这里插入图片描述

def merge_duplicates(records, key_field):
    """合并重复记录——取所有字段的非空并集"""
    merged = {}
    
    for record in records:
        key = record[key_field]
        
        if key not in merged:
            merged[key] = record.copy()
        else:
            # 合并非空字段
            existing = merged[key]
            for field, value in record.items():
                if value and not existing.get(field):
                    existing[field] = value  # 用新值填充空字段
    
    return list(merged.values())

# 示例
records = [
    {"id": "001", "name": "张三", "phone": "13800138000", "email": ""},
    {"id": "002", "name": "李四", "phone": "", "email": "lisi@qq.com"},
    {"id": "001", "name": "张三", "phone": "", "email": "zhangsan@qq.com"},
]

merged = merge_duplicates(records, "id")
# 结果:张三的信息现在有完整的phone和email

方法六:时间优先去重

适合:同一ID有多条历史记录,保留最新的。

from datetime import datetime

def keep_latest(records, key_field, time_field):
    """按关键字段去重,保留时间最新的"""
    latest = {}
    
    for record in records:
        key = record[key_field]
        record_time = record.get(time_field)
        
        # 解析时间
        if isinstance(record_time, str):
            try:
                record_time = datetime.strptime(record_time, "%Y-%m-%d %H:%M:%S")
            except:
                record_time = datetime.min
        
        if key not in latest:
            latest[key] = record
        else:
            existing_time = latest[key].get(time_field)
            if isinstance(existing_time, str):
                try:
                    existing_time = datetime.strptime(existing_time, "%Y-%m-%d %H:%M:%S")
                except:
                    existing_time = datetime.min
            
            if record_time > existing_time:
                latest[key] = record
    
    return list(latest.values())

方法七:权重优先去重

在这里插入图片描述

适合:多源数据去重,不同来源的可信度不同。

def keep_highest_quality(records, key_field, source_weights):
    """
    按来源权重去重——保留可信度最高的
    source_weights: {"官方数据库": 10, "用户提交": 3, "公开采集": 1}
    """
    best = {}
    
    for record in records:
        key = record[key_field]
        source = record.get("source", "未知")
        weight = source_weights.get(source, 0)
        
        if key not in best:
            best[key] = (weight, record)
        else:
            existing_weight, _ = best[key]
            if weight > existing_weight:
                best[key] = (weight, record)
    
    return [record for _, record in best.values()]

有什么坑

坑一:set去重后顺序乱了

现象:用 set() 去重后列表顺序和原来不一样了(Python 3.7+的dict保持插入顺序但set不保证)。

在这里插入图片描述

解决:保持顺序用 dict.fromkeys()

unique_ordered = list(dict.fromkeys(items))

坑二:忽略了None和空字符串的区别

现象None"" 被判定为不同的key,导致没去掉重复。

解决:生成组合键时统一规范化空值:

在这里插入图片描述

[video(video-tH9VCwRG-1784132834586)(type-csdn)(url-live.csdn.net/v/embed/524…)]

def normalize_key(value):
    """规范化键值——把各种空值统一"""
    if value is None or str(value).strip() == "":
        return "_EMPTY_"
    return str(value).strip()

坑三:模糊去重在大数据集上超慢

现象:5000条数据做两两模糊比较,O(n²)复杂度,流程卡死。

解决:先精确去重,再按首字母或拼音分组缩小候选范围。或者用距离算法先聚类。

在这里插入图片描述

坑四:去重决策没有记录

现象:去重后不知道删了什么,出了问题没法回溯。

解决:记录去重日志:

def dedup_with_log(records, key_func):
    """带去重日志"""
    seen = {}
    duplicates = []
    
    for record in records:
        key = key_func(record)
        if key in seen:
            duplicates.append({"kept": seen[key], "removed": record})
        else:
            seen[key] = record
    
    print(f"去重: {len(records)}{len(seen)}条, 移除{len(duplicates)}条重复")
    
    return list(seen.values()), duplicates

坑五:业务上的"重复"和技术上的"重复"不一致

在这里插入图片描述

现象:程序认为商品ID不同就不是重复,但业务上同一个商品在不同平台有不同ID。

解决:这种情况需要建立"商品映射表"——先做技术去重(按字段),再做业务匹配(按规则),两者分开处理。


总结:去重方法的选择顺序——先用集合/dict按关键字段精确去重(最快),再根据业务需求选择保留策略(第一条、最新、合并、权重),最后才考虑模糊匹配(最慢)。大部分场景用方法一、二、三就够了。每做一次去重,把去重原因和数量记到日志里。 在这里插入图片描述