python3 寻找目标名称中字符串最长匹配实践

版权声明:本文为博主原创文章,欢迎病毒式泛滥! https://blog.csdn.net/qiaokelinaicha/article/details/85317989
  • 项目目标
    已有本地登记数据local-data.xlsx,需从网页填报数据web-data.xls中筛查出未填报的本地数据

  • 数据特征

  1. web-data.xls包含目标列“尾矿库名称”和“企业名称”,local-data.xlsx包含目标列“尾矿库名称”、“所属企业”、“尾矿库\n运行情况”
  2. web-data.xls数据基本格式如下:
尾矿库名称(str) 企业名称(str)
(str) (str)

local-data.xlsx数据基本格式如下:

尾矿库名称(str) 企业名称(str) 尾矿库\n运行情况(str)
(str) (str) 在用/停用/…(str)
  1. web-data.xls的“企业名称”列是“尾矿库名称”列的上级列,即某企业包含某尾矿库,local-data.xlsx的目标列“所属企业”和“尾矿库名称”存在相同关系
  2. 一家企业可包含多个尾矿库,企业名称不允许同名,尾矿库名称不允许同名
  3. web-data.xls应和local-data.xlsx中能匹配的对应条目描述基本一致,即web-data.xls的“尾矿库名称”与local-data.xlsx的“尾矿库名称”对应字符串完全相同或相似,web-data.xls的“企业名称”与local-data.xlsx的“所属企业”对应字符同上
  4. 在网页测试过程中手工填报了一部分条目,未及时删除,与网页发布后填报的数据重复,使web-data.xls中存在同名尾矿库或同名企业,与上文第3点数据特征相违背,此类数据应首先进行筛查处理
  5. local-data.xlsx中“尾矿库\n运行情况”列区分“在用”“停用”等状态,需要从中筛选出状态为“在用”的数据条目
  • 思路原则
    优先匹配尾矿库名称,利用企业名称进行配合比较

  • 解决方案

  1. 筛查处理web-data.xls中存在同名尾矿库或同名企业
  2. 筛选出local-data.xlsx中“尾矿库\n运行情况”为“在用”的数据条目
  3. 处理干净后的web-data.xls和local-data.xlsx的条目进行逐条对比,
  4. 逐条对比中,先删除“尾矿库名称”字符串完全匹配条目,得到第一次对比剩余的web-data和local-data
  5. 第一次对比剩余的web-data和local-data中,删除“企业名称”字符串完全匹配条目,得到第二次对比剩余的web-data和local-data;此时的数据已全部去除完全同名尾矿库名称和完全同名企业名称,仅包含名称相似数据(需进一步筛除)和目标数据(即未填报的本地数据)
  6. 第二次对比剩余的web-data和local-data中,采用循环,对每行数据进行“尾矿库名称+企业名称”字符串最长匹配,具体如下:
  1. 当前行web-data 匹配目标字符串 = str(尾矿库名称) + str(企业名称)
  2. 当前行local-data 匹配目标字符串 = str(尾矿库名称) + str(所属企业)
  3. 对当前行web-data 匹配目标字符串和当前行local-data 匹配目标字符串进行len()比较,令 str_long = len()较长字符串,str_short = len()较短字符串
  4. 将str_short的每个字符character以“滑动窗口形式”向str_long逐个字符进行循环比较,当str_long有str_short当前character时,计数n = n + 1,且从str_long中删除该字符,滑动str_short字符窗口进行下一个循环;最后取当前web-data对当前local-data的最长匹配字符n,并计算n = float( n / len(str_short) );此处,因str_short实际为字符串列表list,所以循环当前str_short的包含字符character时为依次循环,表现为“滑动窗口形式”,且字符匹配时无需删除,而str_long虽同为字符串列表,但因其为被比较对象,若在字符匹配时不删除当前匹配字符,则进入当前str_short的下一个character循环时会重新计数,导致计数错误,所以每匹配一次character均删除当前str_long中的匹配character
  5. 对于每行web-data,可得到多个local-data的匹配值,取最大n值和对应local-data作为当前行web-data的匹配信息,登记入匹配字典match_dic;对于最大n值相等的情况,此处采取先出现先匹配的原则,取第一个出现的最大n值作为匹配值
  1. 除去第二次对比剩余的web-data和local-data中在match_dic中出现的记录,得到第三次对比剩余的web-data和local-data
  2. 当web-data的记录条目不为0时,进行第四次对比,参考解决方案5的思路对每行数据进行“企业名称”字符串最长匹配,得到第四次对比剩余的web-data和local-data
  3. web-data的记录条目为0,匹配全部完成
  • 解决步骤
  1. 读取数据
  2. 筛除完全同名“尾矿库名称”数据
  3. 筛除完全同名“企业名称”数据
  4. “尾矿库名称+企业名称”数据匹配筛除
  5. “企业名称”数据匹配筛除
  6. 输出结果
  • 核心代码
  1. 筛查web-data.xls中的重复数据
def getRepeatData(data_dic, tail_data, already_exist_data, i):
    for (k, v) in list(data_dic.items()):  
    # dictionary changed size during iteration => 字典转换为集合或列表
        if '-repeat' in k:
            k = str(k).split('-')[0]
        if tail_data == k:
            already_exist_data.append([tail_data, i])
            break
  1. 除去同名尾矿库
    for (k_web,v_web) in list(data_dic_web.items()):
        if k_web in data_dic_local.keys():
            del data_dic_local[k_web]
            del data_dic_web[k_web]
  1. 除去同名尾矿库后,在web剩余中先对比local查找同企业名称尾矿库
    for (k_web,v_web) in list(data_dic_web.items()):
        for(k_local,v_local) in list(data_dic_local.items()):
            if v_web == v_local:
                # 避免一个企业多个尾矿库、增加字典报错的情况 分别做try
                try:
                    temp_dic_local[k_local] = v_local
                except:
                    pass
                try:
                    temp_dic_web[k_web] = v_web
                except:
                    pass
  1. 通过差集获得不同名且企业名称不同的尾矿库
    for (k_web,v_web) in list(data_dic_web.items()):
        if k_web in temp_dic_web.keys():
            del data_dic_web[k_web]
    for (k_local,v_local) in list(data_dic_local.items()):
        if k_local in temp_dic_local.keys():
            del data_dic_local[k_local]
  1. 对剩余部分,用web数据依次遍历local数据,按最大匹配(最多相同字符)推荐对应匹配项目
# 获取匹配字典,比较尾矿库+企业名称
    match_dic = advancedCompare('all',data_dic_web, data_dic_local)
    # 获取剩余数据
    (data_dic_web, data_dic_local) = getLeftData(match_dic, data_dic_web, data_dic_local)
    
    # 加一层循环,比较企业名称
    if len(data_dic_web) > 0:
        match_dic = advancedCompare('value_only',data_dic_web, data_dic_local)
        (data_dic_web,data_dic_local) = getLeftData(match_dic,data_dic_web,data_dic_local)

# 深入的语义比较
def advancedCompare(pattern,data_dic_web,data_dic_local):
    match_dic = {}
    for item_web in data_dic_web.items():
        temp_match_max_n = 0
        temp_match_max_item = ''
        for item_local in data_dic_local.items():
            n = 0
            # web 和 local 双向比较,短str向长str比较,获得最长匹配 n ,取 n = n / len(较短str)
            str_web = ''
            str_local = ''
            if pattern == 'all':  # 尾矿库名称 + 企业名称
                str_web = str(item_web[0]) + str(item_web[1])
                str_local = str(item_local[0]) + str(item_local[1])
            if pattern == 'value_only':  # 仅企业名称
                str_web = str(item_web[1])
                str_local = str(item_local[1])
            str_long = ''
            str_short = ''
            if len(str_web) >= len(str_local):
                str_long = str_web
                str_short = str_local
            else:
                str_long = str_local
                str_short = str_web
            for character in str_short:  # item 为 tuple
                if character in str_long:
                    n = n + 1
                    # 删除匹配character的第一个字符,避免重复比较
                    str_long = str_long.replace(character, '', 1)
            n = float(n/len(str_short))
            if n > temp_match_max_n:
                temp_match_max_n = n
                temp_match_max_item = item_local

        # local出现重复记录的,即temp_match_max_item,且 n 不相等的,取 n 最大值匹配
        if match_dic == {}:
            match_dic[item_web[0]] = ([item_web, temp_match_max_item, temp_match_max_n])
        else:
            add_flag = True
            for v in list(match_dic.values()):
                # 遍历到最后,扫描一遍
                # 若在字典中有重复匹配的情况
                if temp_match_max_item == v[1]:
                    # 扫描一遍发现相同项,则不执行最后的add
                    add_flag = False
                    # 若 n 值较大,则删除已登记的较小匹配条目,新增较大条目
                    if temp_match_max_n > v[2]:
                        # print("get greater n = {0}, temp_match_max_item = {1}".format(temp_match_max_n,
                        #                                                              temp_match_max_item))
                        del match_dic[v[0][0]]
                        match_dic[item_web[0]] = ([item_web, temp_match_max_item, temp_match_max_n])
                    # 若 n 值相等或变小,则跳过,不添加入字典(相等情况应进一步讨论)
                    else:
                        # print("get lesser {0} {1}".format(temp_match_max_item,temp_match_max_n))
                        pass

            # 扫描一遍,若不在字典中则直接添加
            if add_flag:
                match_dic[item_web[0]] = ([item_web, temp_match_max_item, temp_match_max_n])
    return match_dic

**代码下载地址 **:
https://github.com/793298337/str_longest_compare

猜你喜欢

转载自blog.csdn.net/qiaokelinaicha/article/details/85317989