1. 程式人生 > >python3 尋找目標名稱中字串最長匹配實踐

python3 尋找目標名稱中字串最長匹配實踐

  • 專案目標
    已有本地登記資料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