登録してあるレコードが、マスタに登録があるかをチェックする要件があったので実装してみました。
完全一致する必要はなかったので、ほどほど合致している値がマスタにあるのかを判定したいときに、今回の用途にバッチリあったライブラリを見つけました。
利用したのはdifflibです。
https://docs.python.jp/3/library/difflib.html#difflib.get_close_matches
SequenceMatcherを利用したマッチング
チェックする対象のデータ
GOOGLE inc
google
COMPANY
google technology
マスタに登録してあるデータ
GOOGLE
GOOGLE INC
goo
GOOGLE X
GOOD
ロジック
まず、マスタをリストにします。その後、チェック対象のデータを1つずつループで回して、マスタに当てて、類似度をチェックするロジックです。
from __future__ import with_statement
import difflib
import numpy as np
# マスタデータを内包表記でmasterListに入れる
with open("/Desktop/python/master.csv") as f:
masterList=[line for line in f]
# チェック対象のデータを1つずつ、マスタデータ全体の値と当てる
with open("/Desktop/python/registration.csv") as f:
for registrationLine in f:
# チェック対象のデータ1つに対して、マスタを全ての値の類似度を計算
# upper()を利用して、大文字小文字の差を無くす
ratioList = [difflib.SequenceMatcher(None, registrationLine.upper(), masterLine.upper()).ratio() for masterLine in masterList]
# 類似度が一番大きいデータ値とインデックスを取得する。複数ある場合は先頭の値
npRatioList = np.array(ratioList)
maxRatio = max(npRatioList)
maxRatioIndex = npRatioList.argmax()
# 類似度が0.8より大の場合はマスタに登録あり、小の場合は登録なし、とする
if maxRatio > 0.80:
print("登録あり:" + registrationLine.rstrip() + ":" + masterList[maxRatioIndex].rstrip() + ":" + str(maxRatio))
else:
print("登録なし:" + registrationLine.rstrip() + ":" + masterList[maxRatioIndex].rstrip() + ":" + str(maxRatio))
結果
登録あり:GOOGLE inc:GOOGLE INC:1.0
登録あり:google:GOOGLE:1.0
登録なし:COMPANY:goo:0.333333333333
登録なし:google technology:GOOGLE INC:0.571428571429
思っていた通りの結果になりました。名寄せテーブルなんかも作れそうです。
ただ、仕事で使ったデータでは、いまいちな結果も散見されました。例えば「red engineering」と「blue engineering」は異なる会社ですが、engineeringという長い文字列が一致しているため、類似度が高くなってしまいました。
ちなみにratio()よりも精度が低くて速いquick_ratio()とreal_quick_ratio()があるのですが、これらは精度が低すぎたので使えませんでした。
業務ではこのSequenceMatcherは遅すぎて利用できず、get_close_matchesを利用して解決しました。
データが少なければ、SequenceMatcherでも全然okかと思います。
get_close_matchを使ったロジックは別で書きます。