python3 尋找目標名稱中字串最長匹配實踐
阿新 • • 發佈:2018-12-28
-
專案目標:
已有本地登記資料local-data.xlsx,需從網頁填報資料web-data.xls中篩查出未填報的本地資料 -
資料特徵:
- web-data.xls包含目標列“尾礦庫名稱”和“企業名稱”,local-data.xlsx包含目標列“尾礦庫名稱”、“所屬企業”、“尾礦庫\n執行情況”
- web-data.xls資料基本格式如下:
尾礦庫名稱(str) | 企業名稱(str) |
---|---|
(str) | (str) |
local-data.xlsx資料基本格式如下:
尾礦庫名稱(str) | 企業名稱(str) | 尾礦庫\n執行情況(str) |
---|---|---|
(str) | (str) | 在用/停用/…(str) |
- web-data.xls的“企業名稱”列是“尾礦庫名稱”列的上級列,即某企業包含某尾礦庫,local-data.xlsx的目標列“所屬企業”和“尾礦庫名稱”存在相同關係
- 一家企業可包含多個尾礦庫,企業名稱不允許同名,尾礦庫名稱不允許同名
- web-data.xls應和local-data.xlsx中能匹配的對應條目描述基本一致,即web-data.xls的“尾礦庫名稱”與local-data.xlsx的“尾礦庫名稱”對應字串完全相同或相似,web-data.xls的“企業名稱”與local-data.xlsx的“所屬企業”對應字元同上
- 在網頁測試過程中手工填報了一部分條目,未及時刪除,與網頁釋出後填報的資料重複,使web-data.xls中存在同名尾礦庫或同名企業,與上文第3點資料特徵相違背,此類資料應首先進行篩查處理
- local-data.xlsx中“尾礦庫\n執行情況”列區分“在用”“停用”等狀態,需要從中篩選出狀態為“在用”的資料條目
-
思路原則:
優先匹配尾礦庫名稱,利用企業名稱進行配合比較 -
解決方案:
- 篩查處理web-data.xls中存在同名尾礦庫或同名企業
- 篩選出local-data.xlsx中“尾礦庫\n執行情況”為“在用”的資料條目
- 處理乾淨後的web-data.xls和local-data.xlsx的條目進行逐條對比,
- 逐條對比中,先刪除“尾礦庫名稱”字串完全匹配條目,得到第一次對比剩餘的web-data和local-data
- 第一次對比剩餘的web-data和local-data中,刪除“企業名稱”字串完全匹配條目,得到第二次對比剩餘的web-data和local-data;此時的資料已全部去除完全同名尾礦庫名稱和完全同名企業名稱,僅包含名稱相似資料(需進一步篩除)和目標資料(即未填報的本地資料)
- 第二次對比剩餘的web-data和local-data中,採用迴圈,對每行資料進行“尾礦庫名稱+企業名稱”字串最長匹配,具體如下:
- 當前行web-data 匹配目標字串 = str(尾礦庫名稱) + str(企業名稱)
- 當前行local-data 匹配目標字串 = str(尾礦庫名稱) + str(所屬企業)
- 對當前行web-data 匹配目標字串和當前行local-data 匹配目標字串進行len()比較,令 str_long = len()較長字串,str_short = len()較短字串
- 將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
- 對於每行web-data,可得到多個local-data的匹配值,取最大n值和對應local-data作為當前行web-data的匹配資訊,登記入匹配字典match_dic;對於最大n值相等的情況,此處採取先出現先匹配的原則,取第一個出現的最大n值作為匹配值
- 除去第二次對比剩餘的web-data和local-data中在match_dic中出現的記錄,得到第三次對比剩餘的web-data和local-data
- 當web-data的記錄條目不為0時,進行第四次對比,參考解決方案5的思路對每行資料進行“企業名稱”字串最長匹配,得到第四次對比剩餘的web-data和local-data
- web-data的記錄條目為0,匹配全部完成
- 解決步驟:
- 讀取資料
- 篩除完全同名“尾礦庫名稱”資料
- 篩除完全同名“企業名稱”資料
- “尾礦庫名稱+企業名稱”資料匹配篩除
- “企業名稱”資料匹配篩除
- 輸出結果
- 核心程式碼:
- 篩查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
- 除去同名尾礦庫
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]
- 除去同名尾礦庫後,在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
- 通過差集獲得不同名且企業名稱不同的尾礦庫
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]
- 對剩餘部分,用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