📍
住所文字列の整形(漢数字の丁目表示、表記ゆれ補正)
手持ちの住所リストをジオコーディングする際に、まず住所リストの表記形式やゆれを修正します。
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