1. はじめに

この記事は下記の記事の続きです。

[Python]住所データの正規化(1/2) | 業務で使うPython前処理

1/2では、都道府県や市、番地を分離する方法を紹介しました。この記事では簡単なマスタを使って表現を統一する方法や全角と半角を統一する方法を紹介します。

2. 住所データの現状と課題

表記の揺れと不統一な入力例

  • 全角・半角の混在: 例:「1−2−3」 vs 「1-2-3」
  • 都道府県名の違い: 例:「東京」 vs 「東京都」
  • 区切り記号や不要な空白: 例:「新宿区, 東京」や「大阪府 大阪市」

これらの問題があると、システムでの住所データの検索やマッチングに失敗し、誤配送やデータ集計のエラーにつながります。

3. 統一フォーマット実現のためのアプローチ

統一フォーマットへ変換するためのプロセスは、以下のステップに分けられます。

3.1 前処理 ― 全角・半角の統一

全角数字・記号の半角変換:
例:1、全角カンマ「,」→半角カンマ「,」
Pythonのライブラリ jaconv を利用すると便利です。

3.2 クレンジング ― 不要な記号と空白の削除

正規表現を使い、余分な空白や記号(カンマ、句読点など)を取り除きます。

3.3 分割処理 ― 都道府県、市区町村、番地の抽出

正規表現を用いて、住所データを各要素に分割します。
※都道府県は、正式名称のパターンに合わせるか、後続の辞書変換で整合性を取ります。

3.4 正式名称への変換 ― 正規化辞書の活用

正規化辞書:
「東京」と入力された場合に「東京都」と統一するため、辞書(マッピング)を用意します。
例:{"東京": "東京都", "京": "京都府", ...}

市区町村も、必要に応じて正式名称へ変換する処理を追加します。

3.5 再構築 ― 統一フォーマットで住所を再構成

上記処理後の要素を「都道府県 市区町村 番地」という形式に整形します。

4. Pythonで実装する完全な住所正規化のコード例

以下は、上記のアプローチを踏まえたサンプルコードです。
※実際の業務に合わせて、正規表現パターンや辞書内容を調整してください。

import re
import jaconv  # pip install jaconv

# 正規化辞書(都道府県の例)
PREFECTURE_DICT = {
    "東京": "東京都",
    "東京都": "東京都",
    "大阪": "大阪府",
    "大阪府": "大阪府",
    "京都": "京都府",
    "京都府": "京都府",
    "北海道": "北海道",
    # 必要に応じて他の都道府県も追加
}
def normalize_fullwidth_to_halfwidth(text):
    """
    全角文字を半角に変換します。
    """
    return jaconv.z2h(text, kana=False, digit=True, ascii=True)

def clean_address(address):
    """
    不要な記号や余分な空白を削除し、統一した形に変換する。
    """
    # 全角→半角変換
    address = normalize_fullwidth_to_halfwidth(address)
    # 全角カンマを半角カンマに統一(上記変換で済む場合もありますが念のため)
    address = address.replace(',', ',')
    # 余分な空白、タブ、改行の削除
    address = re.sub(r'\s+', ' ', address).strip()
    return address

 
import re 
def split_address(address): 
    """ 正規表現で都道府県、市区町村、番地を抽出します。 
    ※パターンはシンプルな例です。実際の住所パターンに合わせて調整してください。 """ 
    # 例:都道府県(「東京都」「大阪府」など)をオプションで抽出 
    # 市区町村は「〇〇市」「〇〇区」「〇〇町」などを想定 
    pattern = r"^(?P<pref>.+?[都道府県]|北海道)?\s*(?P<city>.+?[市区町村])\s*(?P<rest>.*)$" 
    match = re.match(pattern, address) 
    if match: 
        return match.group('pref') or '', match.group('city') or '', match.group('rest') or '' 
    return '', '', address # 抽出できなかった場合は全体を返す 
def normalize_prefecture(pref):
    """
    正規化辞書を利用して都道府県名を正式名称に変換します。
    """
    pref = pref.strip()
    return PREFECTURE_DICT.get(pref, pref)  # 辞書にない場合はそのまま返す

def normalize_address(address):
    """
    住所データを完全に正規化し、統一フォーマット「都道府県 市区町村 番地」に変換する。
    """
    # 1. 前処理:全角→半角、不要な空白・記号の削除
    cleaned = clean_address(address)
    
    # 2. 分割:都道府県、市区町村、番地部分に分ける
    pref, city, rest = split_address(cleaned)
    
    # 3. 正式名称への変換(都道府県のみ実施。市区町村も必要なら同様の辞書を適用)
    pref = normalize_prefecture(pref)
    
    # 4. 不要なカンマ等の削除(番地部分のクレンジング)
    rest = rest.replace(',', ' ').strip()
    
    # 5. 統一フォーマットに再構築
    normalized = f"{pref} {city} {rest}".strip()
    # 複数スペースが生じた場合を防止
    normalized = re.sub(r'\s+', ' ', normalized)
    return normalized

# サンプル入力と出力例
addresses = [
    "東京都新宿区",             # 基本例
    "新宿区, 東京",             # 順番が異なる例
    "大阪 大阪市, 1-2-3",       # 全角空白と記号の例
    "北海道札幌市 1−2−3",     # 全角数字の例
    "東京 新宿区 3-4-5"      # 都道府県の表記ゆれ
]

for addr in addresses:
    normalized_addr = normalize_address(addr)
    print(f"入力: {addr}\n正規化後: {normalized_addr}\n")

入力: 東京都新宿区
正規化後: 東京都 新宿区

入力: 新宿区, 東京
正規化後: 東京都 新宿区

入力: 大阪 大阪市, 1-2-3
正規化後: 大阪府 大阪市 1-2-3

入力: 北海道札幌市 1−2−3
正規化後: 北海道 札幌市 1-2-3

入力: 東京 新宿区 3-4-5
正規化後: 東京都 新宿区 3-4-5

5. エラー処理と応用編

5.1 エラー処理

抽出失敗時のログ出力:
正規表現でマッチしなかった住所については、ログに記録して後で手動チェックできる仕組みを導入することが有用です。
欠損値の処理: pandasを用いる場合、fillnadropnaを活用し、欠損値を補完または除外する処理も検討してください。

5.2 異なるフォーマットのデータ対応

多様な表記パターンへの対応:
複数の正規表現パターンを試す、または柔軟なルールセットを設けることで、入力のばらつきに対応できます。
市区町村の正規化辞書:
必要に応じて、都道府県と同様に市区町村の正式名称を補正する辞書を作成し、適用します。

6. まとめ

本記事では、住所データを分割し、全角・半角の統一、不要な記号・空白の削除、簡単なマスタを使って正式名称への変換しました。