あいまいな住所を補正する
- 誤字・脱字のある住所情報を補正したい。
- 番地・マンション名などは対象外
使うもの
- python3.6
- pythonのngramライブラリ: python-ngram
pip install ngram
- 日本郵便の住所データ
- http://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html
- 番地情報によって複数行になっているところを1行として作成する
ソース
動けばいい方針です
住所マスタ作成
def create_address_master()
"""郵便番号データから住所マスタCSVを作成"""
zip_file = os.path.join(os.path.dirname(__file__), 'ken_all.zip')
csv_file = os.path.join(os.path.dirname(__file__), 'KEN_ALL.CSV')
mst_file = os.path.join(os.path.dirname(__file__), 'addr_mst.csv')
if not os.path.exists(csv_file) and os.path.exists(zip_file):
with zipfile.ZipFile(zip_file, 'r') as f:
f.extractall(os.path.dirname(__file__))
with open(csv_file, 'r', encoding="shift-jis") as f:
csv_reader = list(csv.reader(f, delimiter=','))
result = []
_tmp = ""
for v in csv_reader:
_addr = v[6] + v[7]
if v[8] == "以下に掲載がない場合":
v[8] = ""
elif v[8].find("(") != -1:
if v[8].find(")") != -1:
# 閉じ括弧があるので1行完結: "("より前の部分だけ使用する
# ex) L4
# print("case1-1: ")
v[8] = v[8][:v[8].find("(")]
# print(v[8])
else:
# 閉じ括弧がないので、次に閉じ括弧が出て来る行を使用すれば良い
# ex) L3088
# print("case1-2: ")
_tmp = v[8][:v[8].find("(")]
# print(_tmp)
continue
elif v[8].find(")") != -1:
# 開き括弧がなく閉じ括弧ある = 前の行からの続き、かつこの行で終わり
# ex) L3090
# print("case2: ")
v[8] = _tmp
_tmp = ""
# print(v[8])
elif len(_tmp):
# 開き括弧も閉じ括弧もないのに _tmp が空でない => 括弧の途中なので不要な行
# ex) L3089
# print("case3: ")
continue
_addr += v[8]
# 前の行と同じならスキップ
if len(result) > 1 and _addr == result[-1]:
continue
# print(_addr)
result.append(_addr)
with open(mst_file, 'w', encoding="utf-8") as f:
f.write("\n".join(result))
あいまい検索実行
def complement_address(addr: str) -> str:
"""n-gramであいまい検索"""
addr_num = re.search("[0-9]", addr)
word = addr[:addr_num.start()] if addr_num else addr
n = NGram([word], N=2, warp=3)
csv_file = os.path.join(os.path.dirname(__file__), 'addr_mst.csv')
if not os.path.exists(csv_file):
create_address_master()
with open(csv_file, 'r', encoding="utf-8") as f:
addr_mst_str = f.read()
addr_mst_list = addr_mst_str.split("\n")
probability = 0.0
_addr = ""
for v in addr_mst_list:
res = n.search(v)
if len(res) == 0:
continue
if probability < res[0][1]:
probability = res[0][1]
_addr = v
# 元データから番地以降を取得
if addr_num:
_addr += addr[addr_num.start():]
return _addr
動作確認
正解例
>>> print(complement_address("木京都豊鳥区河袋2-65-6"))
東京都豊島区池袋9-99-9
不正解例
>>> print(_complement_address("木京都豊鳥区沼袋2-65-6"))
東京都中野区沼袋9-99-9
-
豊鳥区沼袋
に対して中野区沼袋
,豊島区池袋
ともに2文字違いなので、先にヒットした結果が表示されている
課題
- 不一致な文字数が同じ場合の判断をどうするか?
- 番地の構成から判断?
- 文字の作りが似ている?