【Python】住所から郵便番号への変換

# はじめに
郵便番号を住所に変換するコードはよく見かけますが、住所から郵便番号の変換はあまり参考になる記事がなかったのでプログラムを作成しました。

この記事では作成したコードについての説明を行っています。

# 作ったもの
作成したコードはgithubに載せてあります。

https://github.com/masa2122/address_into_postcode/tree/main

# 全体の流れ
下記の流れで処理を行っていきます。
1. 郵便番号のデータを読み込みを行う
2. 入力された文字列から家屋番号などを消す
3. 住所を検索する
### 使用ライブラリー
- Pandas
- unicodedata
- re
## 郵便番号のデータを読み込む
データを読み込み、 _city列_ と _town列_ を結合し address列を作成しています。この列は住所を探す時に使用します。
```python:main.py
import pandas as pd
import unicodedata
import re

# 郵便番号の設定
post_df = pd.read_csv('郵便番号.csv',encoding = "shift-jis", header=None, names=['post_code', 'prefecture', 'city', 'town'])
post_df['address'] = post_df['city'] + post_df['town'].fillna('')

```

データは日本郵便は出しているものを加工して使用しています。

https://www.post.japanpost.jp/zipcode/download.html

## メイン処理
コード全体
```python:main.py
class post_code():
    
    def __init__(self):
        self.post_code= 0
        self.score_dict = {}

    def processing(self, address):
        
        #数字を小文字へ
        address_new = unicodedata.normalize('NFKC', str(address))
        # 丁目の前の漢数字を英数字へ
        address_new = address_new.replace('一丁目', '1丁目').replace('二丁目', '2丁目').replace('三丁目', '3丁目').replace('四丁目', '4丁目').replace('五丁目', '5丁目').replace('六丁目', '6丁目').replace('七丁目', '7丁目').replace('八丁目', '8丁目').replace('九丁目', '9丁目')
        address_new = address_new.replace('-', '').replace('ー', '')
        # 丁目、番地などを削除
        address_new = re.sub(r"\d+丁目|\d+番地|\d+番|\d+号", "", address_new)
        address_new = re.sub(r"\d+$", "", address_new)
        return address_new
    
    def to_post(self, address):
        
        prefecture = address[:4]
        if '都' in prefecture:
            prefecture = re.match(r"^(.+?都)",prefecture).group(1)
        elif '県' in prefecture:
            prefecture = re.match(r"^(.+?県)",prefecture).group(1)
        elif '府' in prefecture:
            prefecture = re.match(r"^(.+?府)",prefecture).group(1)
        elif '道' in prefecture:
            prefecture = re.match(r"^(.+?道)",prefecture).group(1)

        
        choose_df = post_df[post_df['prefecture'] == prefecture]
        address_new = address.replace(prefecture, '')

        for index in choose_df.index:
            text = choose_df['address'].loc[choose_df.index == index]
            text = str(text)
            count = 0
            for word in address_new:

                word= str(word)
                if word in text:
                    count += 1
            self.score_dict[index] = count
            
        # print(sorted(self.score_dict.items(), key=lambda x: x[1], reverse=True))
 
        post_code = choose_df['post_code'].loc[choose_df.index == max(self.score_dict, key=self.score_dict.get)].values[0]
        post_code = str(post_code)
        post_code = post_code[:3] + '-' + post_code[3:]
        return post_code

    def run(self, address):
        address_new = self.processing(address)
        self.post_code = self.to_post(address_new)

        return self.post_code
```
### 初期化
検索した住所を入れる _post_code_ と検索時に使用する _score_dict_ を初期化する。

```python:クラス内
def __init__(self):
    self.post_code= 0
    self.score_dict = {}
```

### 加工メソッドの説明
ここでは郵便番号を検索する際に必要のない情報を以下の処理によって削除し、返り値としています。

検索には基本的には市区町村までを使いますが、 市によってはその後も必要にる場合があります。
1. 数字を全て小文字に変換する

2. 漢数字の丁目を英数字へ変更

3. ハイフンの削除

4. 丁目や地番などを数字と共に削除

5. 消し残しのための数字削除処理

```python:クラス内
def processing(self, address):
        
    #数字を小文字へ
    address_new = unicodedata.normalize('NFKC', str(address))
    # 丁目の前の漢数字を英数字へ
    address_new = address_new.replace('一丁目', '1丁目').replace('二丁目', '2丁目').replace('三丁目', '3丁目').replace('四丁目', '4丁目').replace('五丁目', '5丁目').replace('六丁目', '6丁目').replace('七丁目', '7丁目').replace('八丁目', '8丁目').replace('九丁目', '9丁目')
    address_new = address_new.replace('-', '').replace('ー', '')
    # 丁目、番地などを削除
    address_new = re.sub(r"\d+丁目|\d+番地|\d+番|\d+号", "", address_new)
    address_new = re.sub(r"\d+$", "", address_new)
    return address_new
```

### 検索メソッドの説明
簡単に処理の説明をします。

1. はじめの４文字を使用して都道府県を探し出し、その値を使用して郵便番号データから完全一致する行を取り出し _choose_df_ に代入する。

2. _prefecture_ の値を _address_ から消す。

3. _choose_df_　のaddress列から一つずつ値を取り出し、 _address_new_ と何文字一致しているかを _score_dict_ に格納する。

4. 最後に _score_dict_ の中で一致した文字数が一番多い行から郵便番号を取り出し、ハイフンを付けて返す。

```python:クラス内
def to_post(self, address):
        
    prefecture = address[:4]
    if '都' in prefecture:
        prefecture = re.match(r"^(.+?都)",prefecture).group(1)
    elif '県' in prefecture:
        prefecture = re.match(r"^(.+?県)",prefecture).group(1)
    elif '府' in prefecture:
        prefecture = re.match(r"^(.+?府)",prefecture).group(1)
    elif '道' in prefecture:
        prefecture = re.match(r"^(.+?道)",prefecture).group(1)

    
    choose_df = post_df[post_df['prefecture'] == prefecture]
    address_new = address.replace(prefecture, '')

    for index in choose_df.index:
        text = choose_df['address'].loc[choose_df.index == index]
        text = str(text)
        count = 0
        for word in address_new:

            word= str(word)
            if word in text:
                count += 1
        self.score_dict[index] = count
        
 
    post_code = choose_df['post_code'].loc[choose_df.index == max(self.score_dict, key=self.score_dict.get)].values[0]
    post_code = str(post_code)
    post_code = post_code[:3] + '-' + post_code[3:]
    return post_code
```

### 実行メソッド
```python:main.py
def run(self, address):
    address_new = self.processing(address)
    self.post_code = self.to_post(address_new)

    return self.post_code
```

### 使用方法
runのに検索した住所を都道府県込みで入力してください。
```python:
raku_df = post_code().run('東京都港区白金1-2-3')
print(raku_df)
# '108-0072'
```
# 終わりに
Pythonで住所から郵便番号を検索するプログラムを作成しました。
今後の課題として、処理の高速化があります！

初学者のため情報に誤りがある可能せいがあります。
ご了承ください。