📍

住所文字列の整形(漢数字の丁目表示、表記ゆれ補正)

に公開

手持ちの住所リストをジオコーディングする際に、まず住所リストの表記形式やゆれを修正します。

import re
import unicodedata

def convert_address_chome(address_string: str) -> str:
    """
    住所文字列に含まれる漢数字以外の丁目表記を「〇丁目」形式に変換します。

    本関数は、住所表記の揺れ(全角/半角、ハイフンの種類)を吸収しつつ、
    以下の3つのパターンで丁目表記を修正・挿入することで、住所の正規化を試みます。
    
    Parameters
    ----------
    address_string : str
        処理対象の住所文字列(例: '〇〇1丁目1-1', '〇〇1-1-1', '〇〇1-1')。

    Returns
    -------
    str
        「丁目」表記が正規化された住所文字列。
        丁目と見なされる数字が漢数字に変換され、「丁目」が挿入されます。

    Notes
    -----
    1.  **表記揺れの吸収**: 
        - 全角/半角、空白文字の削除、各種ハイフン記号(‐-―ー)の半角ハイフン(-)への統一を行います。
        - 変換処理は、**正規化後の半角数字/半角ハイフン**に対して行われます。
    2.  **変換ロジック**: 
        変換は以下の優先順位で行われ、最初に一致したパターンのみが適用されます。
        
        * **パターン 1 (最優先): 既存の「丁目」表記の数字変換**
            - 住所に既に **「[半角数字]丁目」** が含まれる場合 (例: '1丁目')。
            - **数字のみ**を漢数字(一〜九)に変換します (例: '1丁目' -> '一丁目')。
            
        * **パターン 2: 3桁以上のハイフン区切りの中央に「丁目」を挿入**
            - **「-〇-」** のパターン(例: '1-1-1' の1-**1**-1など)に一致し、
              2番目のハイフンの直前に数字がある場合。
            - 最初のハイフンの直前の数字を漢数字に変換し、**「丁目」を挿入**します。
            - (例: '1-1-1' -> '一丁目1-1'、ただしコードのロジックでは '1-1-1' -> '一丁目-1')
              *元のコードの正規表現 `r'-\w+-'` は、`X-Y-Z` の `Y-Z` の部分を検出するため、
               実際には `1-1-1` の `1` を漢数字に変換し、「丁目」を挿入した後に
               `1-1` を削除する処理が行われます。(例:'1-1-1' $\to$ '一丁目-1')
            
        * **パターン 3: 2桁のハイフン区切りに「丁目」を挿入**
            - **「[^1-9][1-9]-[1-9]」** のパターン(例: '〇1-1')に一致する場合。
            - 最初の数字(町域の直後の数字)を漢数字に変換し、**「丁目」を挿入**します。
            - (例: '〇1-1' -> '〇一丁目1')
            
    3.  **注意点**: 漢数字(一〜九)以外の数字(例: 十、二十など)は対応していません。
    """
    # 1. 文字正規化で、全角/半角やハイフンの種類を吸収する
    # これにより、以降の処理では半角数字と半角ハイフンだけを考えれば良くなる
    string = unicodedata.normalize('NFKC', address_string)
    string = string.replace(' ', '')
    string = re.sub(r'[‐-―ー]', '-', string)

    # 2. 変換テーブルを半角数字からの変換に修正
    # '1'..'9' -> '一'..'九'
    tbl_2kansu = str.maketrans('123456789', '一二三四五六七八九')
    # tbl_2kansu = str.maketrans('123456789', '一二三四五六七八九')
    
    # 3. 元のコードのif/elifロジックを、正規化後の文字列に対して適用
    #    正規表現内の全角数字も半角に修正
    if '丁目' in string:
        # 「〇丁目」の前の数字を漢数字に変換 (例: "1丁目" -> "一丁目")
        # re.subを使うと、文字列スライスより少しだけシンプルに書ける
        string = re.sub(r'([1-9])丁目', lambda m: m.group(1).translate(tbl_2kansu) + '丁目', string)
        # idx = string.find('丁目'); string = string[:idx-1] + string[idx-1].translate(tbl_2kansu) + string[idx:]

    elif re.search(r'-\w+-', string):
    # elif re.search(r'[‐-―ー]+\w+[‐-―ー]', string):
        # 〇-〇-〇 のようなパターンの最初の数字を丁目に変換
        idx = re.search(r'-\w+-', string).start()
        string = string[:idx-1] + string[idx-1].translate(tbl_2kansu) + '丁目' + string[idx+1:]
        # idx = re.search(r'[‐-―ー]+\w+[‐-―ー]', string).start(); string = string[:idx-1] + string[idx-1].translate(tbl_2kansu) + '丁目' + string[idx+1:]
        
    elif re.search(r'[^1-9][1-9]-[1-9]', string):
    # elif re.search(r'[^123456789][123456789][‐-―ー][123456789]', string):
        # 〇1-2 のようなパターンの数字を丁目に変換
        idx = re.search(r'[^1-9][1-9]-[1-9]', string).start()
        # idx = re.search(r'[^123456789][123456789][‐-―ー][123456789]', string).start()
        string = string[:idx+1] + string[idx+1].translate(tbl_2kansu) + '丁目' + string[idx+2:]
    
    return string

入力辞書で変換

10丁目などには、個別辞書をつくって対応します。

### 個別辞書による変換処理 ###
def convert_by_private_dict(string, _dict):
    for key, value in _dict.items():
        if key in string: # maybe not needed
            string = string.replace(key, value)
    return string

### 個別辞書 ###
dict_f = {
    '10丁目':'十丁目', 
    '糟屋郡須惠町':'糟屋郡須恵町',
    '粕屋郡':'糟屋郡',
    'ひばりヶ丘':'ひばりが丘'
}

以下のように入力住所データを変換します。

arr_converted_address = np.array([convert_address_chome(item) for item in input_address])
arr_converted_address = np.array([dict_f(item) for item in arr_coverted_address])

Discussion