Pythonであいまいな住所を補正したい

# あいまいな住所を補正する

* 誤字・脱字のある住所情報を補正したい。
* 番地・マンション名などは対象外

# 使うもの

* python3.6
* pythonのngramライブラリ: [python-ngram](https://github.com/gpoulter/python-ngram)
    * `pip install ngram`
* 日本郵便の住所データ
    * http://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html
    * 番地情報によって複数行になっているところを１行として作成する
        * 参考: [日本郵便の郵便番号データを解析してみる　第２回～括弧の意味編～](http://www.my-hobby.jp/2010/03/26/zipsearch2/)

# ソース

動けばいい方針です

### 住所マスタ作成

```python
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:
                # 閉じ括弧があるので１行完結: "（"より前の部分だけ使用する
                # 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))
```

### あいまい検索実行

```python
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
```

# 動作確認

### 正解例

```python
>>> print(complement_address("木京都豊鳥区河袋2-65-6"))
東京都豊島区池袋9-99-9
```

### 不正解例

```python
>>> print(_complement_address("木京都豊鳥区沼袋2-65-6"))
東京都中野区沼袋9-99-9
```

* `豊鳥区沼袋` に対して `中野区沼袋`, `豊島区池袋` ともに2文字違いなので、先にヒットした結果が表示されている

# 課題

* 不一致な文字数が同じ場合の判断をどうするか？
    * 番地の構成から判断？
    * 文字の作りが似ている？