Edited at

表記揺れを考慮して、登録した値がマスタに登録されているかをマッチングしてみた

More than 1 year has passed since last update.

登録してあるレコードが、マスタに登録があるかをチェックする要件があったので実装してみました。

完全一致する必要はなかったので、ほどほど合致している値がマスタにあるのかを判定したいときに、今回の用途にバッチリあったライブラリを見つけました。

利用したのはdifflibです。

https://docs.python.jp/3/library/difflib.html#difflib.get_close_matches


SequenceMatcherを利用したマッチング


チェックする対象のデータ


registration.csv

GOOGLE inc

google
COMPANY
google technology


マスタに登録してあるデータ


master.csv

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を使ったロジックは別で書きます。