Skip to content

Instantly share code, notes, and snippets.

@m77so
Last active March 26, 2023 09:42
Show Gist options
  • Select an option

  • Save m77so/764b0abcb48d818c82dd1935da42c0b4 to your computer and use it in GitHub Desktop.

Select an option

Save m77so/764b0abcb48d818c82dd1935da42c0b4 to your computer and use it in GitHub Desktop.

ChatGPTと一緒にコードを書きます。本日の題材はこちら!

題材

市外局番の一覧の「番号区画」の文字列を使いやすく変換します。

市町村名を列挙した「北海道恵庭市、千歳市」のようなテキストを扱いやすくします。

結果は、「都道府県、政令指定都市または郡、市区町村」の3要素の配列の配列にしたいです。

次の機能をもつ関数を作成します。

(入力例1)北海道恵庭市、千歳市(出力例1)[["北海道", "", "恵庭市"], ["北海道", "", "千歳市"]]

とりあえず書いてみよう。

正直どう指示していいのかわからないので、とりあえずコードを書いてみます(2時間くらい)。

def extract_prefecture(address, pref, city, town):
    # 文脈
    p = pref # 都道府県
    c = city # 郡・政令指定都市
    t = town # 市区町村

    # カッコ内を取り除いておく
    b_index = address.find("(")
    braket = ''
    if b_index >= 0:
      braket = address[b_index:]
      address = address[0:b_index]

    
    # 都道府県を抽出
    prefectures = [
        '北海道', '青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県', '茨城県', '栃木県', '群馬県',
        '埼玉県', '千葉県', '東京都', '神奈川県', '新潟県', '富山県', '石川県', '福井県', '山梨県', '長野県',
        '岐阜県', '静岡県', '愛知県', '三重県', '滋賀県', '京都府', '大阪府', '兵庫県', '奈良県', '和歌山県',
        '鳥取県', '島根県', '岡山県', '広島県', '山口県', '徳島県', '香川県', '愛媛県', '高知県', '福岡県',
        '佐賀県', '長崎県', '熊本県', '大分県', '宮崎県', '鹿児島県', '沖縄県'
    ]

    for prefecture in prefectures:
        if address.startswith(prefecture):
            p = prefecture
            c = None
            t = None
            address = address[len(p):]
            break

    # 自治体名に市や郡を含むので先に処理をしたほうがよい自治体
    sp_cities = [
        '余市郡', '高市郡',  '東村山郡', '西村山郡', '北村山郡', '田村郡', 
        '市川市', '市原市', '野々市市', '四日市市', '廿日市市', '町田市', '十日町市', '大町市', '村山市', '田村市', '東村山市', '武蔵村山市', '羽村市', '村上市', '大村市',
        '郡山市', '郡上市', '蒲郡市',  '大和郡山市', '小郡市', 
        '市川三郷町', '市川町',  '余市町', '市貝町', '上市町', '下市町', '大町町', '村田町', '玉村町', '上郡町', '中村区'
    ]

    # 政令指定都市
    designated_cities = [
        '札幌市', '仙台市', 'さいたま市', '千葉市', '横浜市', '川崎市', '相模原市', '新潟市',
        '静岡市', '浜松市', '名古屋市', '京都市', '大阪市', '堺市', '神戸市', '岡山市', '広島市', '北九州市', '福岡市', '熊本市'
    ]

    # 政令指定都市は先に処理
    for ds_city in designated_cities:
        if address.startswith(ds_city):
            c = ds_city
            address = address[len(c):]
            break

    for sp_city in sp_cities:
        if address.startswith(sp_city):
            if sp_city.endswith("郡"):
                c = sp_city
                t = None
                address = address[len(c):]
                break
            elif sp_city.endswith("市"):
                c = None
                t = sp_city
                address = address[len(t):]
                break
            elif sp_city.endswith("町") or sp_city.endswith("村") or sp_city.endswith("区"):
                t = sp_city
                address = address[len(t):]
                break

    delimiters = ['郡', '市', '町', '村', '区']
    delimiter = None
    d_index = len(address) + 5
    for d in delimiters:
      index = address.find(d)
      if index != -1 and index < d_index:
          d_index = index
          delimiter = d
    if delimiter == '郡':
        c = address[0:d_index+1]
        t = None
        address = address[len(c):]
    elif delimiter is not None:
        t = address[0:d_index+1]
        address = address[len(t):]
    
    if delimiter == '郡':
        for sp_city in sp_cities:
            if address.startswith(sp_city):
                t = sp_city
                address = address[len(t):]
        delimiters = ['町', '村']
        delimiter = None
        d_index = len(address) + 5
        for d in delimiters:
          index = address.find(d)
          if index != -1 and index < d_index:
              d_index = index
              delimiter = d
        if delimiter is not None:
            t = address[0:d_index+1]
            address = address[len(t):]
    return [p, c, t, address + braket]



def get_cities(v):
  print(v.split("、"))
  res = []
  pref = None
  city = None
  town = None
  for c in v.split("、"):
    [pref, city, town, nokori] = extract_prefecture(c, pref, city, town)
    print([pref, city, town, nokori ])
  
for i in range(12):
  get_cities(shigai_list["cities"][i])

書いているうちに次の例に対応する必要が有ることに気づきました。また補足情報として、もう一つ要素があったほうがいいことに気づきます。

(入力例2)北海道江別市、札幌市、北広島市、空知郡南幌町(出力例2)[["北海道","","江別市",""],["北海道","札幌市","",""],["北海道","","北広島市",""],["北海道","空知郡","南幌町",""]]
(入力例3)北海道爾志郡、檜山郡(出力例3)[["北海道","爾志郡","",""],["北海道","檜山郡","",""]]
(入力例4)北海道樺戸郡(浦臼町及び新十津川町に限る。)(出力例4)[["北海道", "樺戸郡", "", "(浦臼町及び新十津川町に限る。)"]]
(入力例5)新潟県十日町市、千葉県市原市(出力例5)[['新潟県','','十日町市',''],['千葉県','','市原市','']]
(入力例6)高知県室戸市、安芸郡東洋町(出力例6)[["高知県","","室戸市",""], ["高知県","安芸郡", "東洋町",""]]

とりあえずGPT-3.5に投げてみる

ダメ元でGPT-3.5に投げます。

🧑‍🌾「リファクタリングして (上のコードをベタ貼り)」

🧠 (3.5)「 以下は、extract_prefecture()関数をリファクタリングしたコードです。主な変更点は、以下の通りです。

  • 関数の引数から、変数 p, c, t を削除し、代わりに文字列 address を直接操作するように変更しました。
  • 都道府県名、政令指定都市名、市区町村名の検出を、正規表現を用いて行うように変更しました。
  • 市区町村名が複数の場合に対応するため、ループ処理を追加しました。
  • 文字列操作に誤りがあったため、修正しました。
import re

def extract_prefecture(address):
    # 文脈
    pref = None # 都道府県
    city = None # 郡・政令指定都市
    town = None # 市区町村

    # 都道府県を抽出
    prefecture_pattern = '^(北海道|(?:京都|大阪)府|[一-龥ぁ-んァ-ン]+県)'
    prefecture_match = re.match(prefecture_pattern, address)
    if prefecture_match:
        pref = prefecture_match.group()
        address = address[prefecture_match.end():]

    # 政令指定都市を抽出
    designated_city_pattern = '|'.join(['^' + city_name + '市' for city_name in designated_cities])
    designated_city_match = re.match(designated_city_pattern, address)
    if designated_city_match:
        city = designated_city_match.group()
        address = address[designated_city_match.end():]

    # 市区町村を抽出
    while True:
        city_pattern = '|'.join(['^(.*郡)?' + city_name + '市' for city_name in sp_cities])
        town_pattern = '|'.join(['^(.*市)?' + town_name + '(区|町|村)' for town_name in sp_cities])
        city_match = re.match(city_pattern, address)
        town_match = re.match(town_pattern, address)
        if city_match:
            city = city_match.group()
            address = address[city_match.end():]
        elif town_match:
            town = town_match.group()
            address = address[town_match.end():]
        else:
            break

    return [pref, city, town]


def get_cities(v):
    print(v.split("、"))
    res = []
    pref = None
    city = None
    town = None
    for c in v.split("、"):
        [pref, city, town] = extract_prefecture(c)
        print([pref, city, town])
    
    
get_cities(shigai_list["cities"][0])

このようにリファクタリングすることで、可読性の高いコードとなり、エラーも修正されているため、より安全な処理が可能となります。

これでは郡上市などが考慮されていません。

気合を入れてGPT-4に投げる

1回目

👨‍🌾「

あなたはPythonの専門家です。以下の条件を満たすPythonのコードを書いていただきたく思います。

市町村名を列挙した「北海道赤平市、歌志内市、砂川市、滝川市、雨竜郡雨竜町、樺戸郡(浦臼町及び新十津川町に限る。)、空知郡(上砂川町及び奈井江町に限る。)」のようなテキストを扱いやすくします。

結果は、「都道府県、政令指定都市または郡、市区町村、補足情報」の4要素の配列の配列にしたいです。

都道府県の配列「prefectures」、政令指定都市の配列「designated_cities」、自治体名に「市」や「町」を含む自治体の配列「sp_cities」は事前に定義されています。

次のように出力します。
(入力例1)北海道恵庭市、千歳市(出力例1)[["北海道", "", "恵庭市",""], ["北海道", "", "千歳市",""]]

市や郡、政令指定都市が混ざることもあります。該当する要素がない場合は空文字を入れます。
(入力例2)北海道江別市、札幌市、北広島市、空知郡南幌町(出力例2)[["北海道","","江別市",""],["北海道","札幌市","",""],["北海道","","北広島市",""],["北海道","空知郡","南幌町",""]]
(入力例3)北海道爾志郡、檜山郡(出力例3)[["北海道","爾志郡","",""],["北海道","檜山郡","",""]]

補足情報は4つめに入れます
(入力例4)北海道樺戸郡(浦臼町及び新十津川町に限る。)(出力例4)[["北海道", "樺戸郡", "", "(浦臼町及び新十津川町に限る。)"]]

「市」「町」など自治体の単位が自治体名に含まれる場合もあります。そのような別途処理をすべき自治体名の配列、「sp_cities」はあらかじめ定義されています。
(入力例5)新潟県十日町市、千葉県市原市(出力例5)[['新潟県','','十日町市',''],['千葉県','','市原市','']]

🧠「 以下は、指定された条件を満たすPythonのコードです。

import re

text = "北海道赤平市、歌志内市、砂川市、滝川市、雨竜郡雨竜町、樺戸郡(浦臼町及び新十津川町に限る。)、空知郡(上砂川町及び奈井江町に限る。)"

# 都道府県、政令指定都市、自治体名を抽出するための正規表現
pattern = r"(?P<prefecture>[\w]+?[都道府県])?(?P<city>[\w]+?[市町村])?(?P<county>[\w]+?[郡])?((?P<sp_city>[" + "|".join(sp_cities) + r"])[\w]+)?"

# 正規表現にマッチする部分を全て抽出する
matches = re.finditer(pattern, text)

result = []
for match in matches:
    prefecture = match.group("prefecture") or ""
    city = match.group("city") or ""
    county = match.group("county") or ""
    sp_city = match.group("sp_city") or ""
    info = ""
    if sp_city:
        # 「市」「町」など自治体の単位が自治体名に含まれる場合
        info = sp_city[len(sp_city) - 1:]
        sp_city = sp_city[:len(sp_city) - 1]
    if county:
        if "限る。" in county:
            # 補足情報がある場合
            county, info = county.split("(")
            info = "(" + info
        result.append([prefecture, county, city, info])
    elif city in designated_cities:
        result.append([prefecture, city, "", info])
    else:
        result.append([prefecture, "", city, info])

print(result)

このコードでは、正規表現を使用して、市町村名から都道府県、政令指定都市、自治体名を抽出しています。そして、それぞれの情報を元に、結果を配列に格納しています。自治体名に「市」や「町」を含む自治体の配列「sp_cities」や、政令指定都市の配列「designated_cities」、都道府県の配列「prefectures」は、あらかじめ定義されていると仮定しています。また、補足情報がある場合にも対応しています。 」

2回目 うーん

「sp_cities以外の町村にうまく対応してください」

「 以下は、指定された条件を満たすPythonのコードです。前回のコードに対して、自治体名に「市」「町」などを含まない場合に対応する処理を追加しています。

import re

text = "北海道赤平市、歌志内市、砂川市、滝川市、雨竜郡雨竜町、樺戸郡(浦臼町及び新十津川町に限る。)、空知郡(上砂川町及び奈井江町に限る。)、秋田県男鹿市、仙北市、雄勝郡東成瀬村"

# 都道府県、政令指定都市、自治体名を抽出するための正規表現
pattern = r"(?P<prefecture>[\w]+?[都道府県])?(?P<city>[\w]+?[市町村])?(?P<county>[\w]+?[郡])?((?P<sp_city>[" + "|".join(sp_cities) + r"])[\w]+)?"

# 正規表現にマッチする部分を全て抽出する
matches = re.finditer(pattern, text)

result = []
for match in matches:
    prefecture = match.group("prefecture") or ""
    city = match.group("city") or ""
    county = match.group("county") or ""
    sp_city = match.group("sp_city") or ""
    info = ""
    if sp_city:
        # 「市」「町」など自治体の単位が自治体名に含まれる場合
        info = sp_city[len(sp_city) - 1:]
        sp_city = sp_city[:len(sp_city) - 1]
    elif county:
        # 郡名が含まれる場合
        if "限る。" in county:
            # 補足情報がある場合
            county, info = county.split("(")
            info = "(" + info
        result.append([prefecture, county, city, info])
    elif city in designated_cities:
        # 政令指定都市の場合
        result.append([prefecture, city, "", info])
    elif city:
        # 「市」「町」など自治体の単位が含まれない場合
        if re.search(r"\d+", city):
            # 数字が含まれる場合は除外する
            continue
        # 郡名が含まれる場合
        county_match = re.search(r"(?P<county>[\w]+?[郡])", city)
        if county_match:
            county = county_match.group("county")
            city = city.replace(county, "")
            result.append([prefecture, county, city, info])
        else:
            result.append([prefecture, "", city, info])

print(result)

3回目 仕切り直し

なんかずれてるので、別のスレッドで仕切り直しです。もう一度やってみましょう。

あなたはPythonの専門家です。以下の条件を満たすPythonのコードを書いていただきたく思います。

市町村名を列挙した「北海道赤平市、歌志内市、砂川市、滝川市、雨竜郡雨竜町、樺戸郡(浦臼町及び新十津川町に限る。)、空知郡(上砂川町及び奈井江町に限る。)」のようなテキストを扱いやすくします。

結果は、「都道府県、政令指定都市または郡、市区町村、補足情報」の4要素の配列の配列にしたいです。

都道府県の配列「prefectures」、政令指定都市の配列「designated_cities」、自治体名に「市」や「町」を含む自治体の配列「sp_cities」は事前に定義されています。

次のように出力します。
(入力例1)北海道恵庭市、千歳市(出力例1)[["北海道", "", "恵庭市",""], ["北海道", "", "千歳市",""]]

市や郡、政令指定都市が混ざることもあります。該当する要素がない場合は空文字を入れます。
(入力例2)北海道江別市、札幌市、北広島市、空知郡南幌町(出力例2)[["北海道","","江別市",""],["北海道","札幌市","",""],["北海道","","北広島市",""],["北海道","空知郡","南幌町",""]]
(入力例3)北海道爾志郡、檜山郡(出力例3)[["北海道","爾志郡","",""],["北海道","檜山郡","",""]]
(入力例6)高知県室戸市、安芸郡東洋町(出力例6)[["高知県","","室戸市",""], ["高知県","安芸郡", "東洋町",""]]

補足情報は4つめに入れます
(入力例4)北海道樺戸郡(浦臼町及び新十津川町に限る。)(出力例4)[["北海道", "樺戸郡", "", "(浦臼町及び新十津川町に限る。)"]]

「市」「町」など自治体の単位が自治体名に含まれる場合もあります。そのような別途処理をすべき自治体名の配列、「sp_cities」はあらかじめ定義されています。
(入力例5)新潟県十日町市、千葉県市原市(出力例5)[['新潟県','','十日町市',''],['千葉県','','市原市','']]

🧠「 以下のPythonコードは、指定された条件を満たすように設計されています。parse_location関数は、入力テキストを受け取り、都道府県、政令指定都市、市区町村、補足情報を含むリストのリストを返します。

import re

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []
    for pref in prefectures:
        if pref in input_text:
            remaining_text = input_text.split(pref)[1]
            break
    
    for segment in remaining_text.split('、'):
        city_or_town = ""
        designated_city = ""
        county = ""
        supplement = ""
        
        for dc in designated_cities:
            if dc in segment:
                designated_city = dc
                segment = segment.replace(dc, '')

        for sp_city in sp_cities:
            if sp_city in segment:
                city_or_town = sp_city
                segment = segment.replace(sp_city, '')
                break
                
        if not city_or_town:
            match = re.search(r'(.*[市町村])', segment)
            if match:
                city_or_town = match.group(1)
                segment = segment.replace(city_or_town, '')
                
        match = re.search(r'(.*[郡])', segment)
        if match:
            county = match.group(1)
            segment = segment.replace(county, '')

        supplement = segment.strip()
        
        result.append([pref, designated_city, city_or_town, supplement])

    return result

# 事前に定義されている配列
prefectures = ['北海道', '新潟県', '千葉県', '高知県']
designated_cities = ['札幌市']
sp_cities = ['十日町市', '市原市']

# 入力例
input_text = '北海道恵庭市、千歳市'
result = parse_location(input_text, prefectures, designated_cities, sp_cities)
print(result)  # [["北海道", "", "恵庭市", ""], ["北海道", "", "千歳市", ""]]

4回目: 筋がいいので修正させる

👨‍🌾「 コードを確認した限りいくつかの問題があります。

1.「佐賀県伊万里市、西松浦郡、長崎県松浦市(鷹島町及び福島町に限る。)」のように、複数の都道府県が交じる場合が考慮されていません。 2.政令指定都市の抽出を、dc in segmentとしていますが、政令指定都市「広島市」がある一方で、北海道には「北広島市」があります。北広島市から広島市を誤って抽出するおそれがあります。 」

🧠「 ご指摘いただいた問題に対処するために、コードを以下のように修正しました。

  • 複数の都道府県が交じる場合に対応するため、parse_location関数を修正して、都道府県ごとに処理するようにしました。
  • 政令指定都市の抽出で誤りが発生しないように、正規表現を使用して、政令指定都市の名称が入力テキスト内で完全一致するものを抽出するように変更しました。

修正後のコードは以下の通りです。

import re

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []

    for pref in prefectures:
        remaining_text = input_text
        if pref in remaining_text:
            remaining_text = remaining_text.split(pref, 1)[1]
            segments = re.split(f'(、(?={pref}))', remaining_text)

            for segment in segments:
                city_or_town = ''
                designated_city = ""
                county = ""
                supplement = ""
                
                for dc in designated_cities:
                    if re.fullmatch(dc, segment.strip()):
                        designated_city = dc
                        segment = segment.replace(dc, '')

                for sp_city in sp_cities:
                    if sp_city in segment:
                        city_or_town = sp_city
                        segment = segment.replace(sp_city, '')
                        break
                
                if not city_or_town:
                    match = re.search(r'(.*[市町村])', segment)
                    if match:
                        city_or_town = match.group(1)
                        segment = segment.replace(city_or_town, '')
                
                match = re.search(r'(.*[郡])', segment)
                if match:
                    county = match.group(1)
                    segment = segment.replace(county, '')

                supplement = segment.strip()
                
                result.append([pref, designated_city, city_or_town, supplement])

    return result

# 事前に定義されている配列
prefectures = ['北海道', '新潟県', '千葉県', '高知県', '佐賀県', '長崎県']
designated_cities = ['札幌市', '広島市']
sp_cities = ['十日町市', '市原市']

#

5回目 実行してみた結果をもとに修正依頼

👨‍🌾「 次のようになってしまい、うまくいきません。修正をお願いします

入力: 北海道枝幸郡(中頓別町及び浜頓別町に限る。) 出力: [['北海道', '', '枝幸郡(中頓別町及び浜頓別町', 'に限る。)']] 入力: 秋田県鹿角市、鹿角郡小坂町 出力: [['秋田県', '', '鹿角市、鹿角郡小坂町', '']] 入力: 山形県鶴岡市、東田川郡三川町 出力: [['山形県', '', '鶴岡市、東田川郡三川町', '']] 」

🧠「 ご指摘いただいた問題に対処するために、コードを以下のように修正しました。これにより、前述の問題が解決されます。

  • 補足情報を正規表現で抽出するように変更しました。
  • 都道府県ごとにテキストを分割する正規表現を追加しました。

修正後のコードは以下の通りです。

import re

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []

    for pref in prefectures:
        if pref in input_text:
            remaining_text = input_text.split(pref, 1)[1]
            segments = re.split(f'(、(?={pref}))', remaining_text)

            for segment in segments:
                city_or_town = ""
                designated_city = ""
                county = ""
                supplement = ""

                supplement_match = re.search(r'(.*?)', segment)
                if supplement_match:
                    supplement = supplement_match.group(0)
                    segment = segment.replace(supplement, '')

                for dc in designated_cities:
                    if re.fullmatch(dc, segment.strip()):
                        designated_city = dc
                        segment = segment.replace(dc, '')

                for sp_city in sp_cities:
                    if sp_city in segment:
                        city_or_town = sp_city
                        segment = segment.replace(sp_city, '')
                        break
                
                if not city_or_town:
                    match = re.search(r'(.*[市町村])', segment)
                    if match:
                        city_or_town = match.group(1)
                        segment = segment.replace(city_or_town, '')
                
                match = re.search(r'(.*[郡])', segment)
                if match:
                    county = match.group(1)
                    segment = segment.replace(county, '')

                result.append([pref, designated_city, city_or_town, supplement])

    return result

# 事前に定義されている配列
prefectures = ['北海道', '新潟県', '千葉県', '高知県', '佐賀県', '長崎県', '秋田県', '山形県']
designated_cities = ['札幌市', '広島市']
sp_cities = ['

6回目 原因を指摘

👨‍🌾「都道府県ごとに「、」で区切った後に、市町村ごとに「、」で区切ってないのが問題ではないでしょうか」 🧠「 おっしゃる通り、その問題が原因でした。都道府県ごとに「、」で区切った後に、市町村ごとに「、」で区切るようにコードを修正しました。また、都道府県が複数ある場合に対応できるよう、コードをさらに修正しました。

修正後のコードは以下の通りです。

import re

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []

    # 都道府県ごとにテキストを分割
    segments_by_pref = re.split(f'(、(?={pref}))', input_text)

    for segment in segments_by_pref:
        for pref in prefectures:
            if pref in segment:
                remaining_text = segment.split(pref, 1)[1]
                break
        else:
            remaining_text = segment

        # 市町村ごとにテキストを分割
        segments = remaining_text.split('、')

        for segment in segments:
            city_or

8回目 全部言い終わる前にダメだし

👨‍🌾「「山梨県甲斐市(岩森、宇津谷、大垈、志田、下今井、菖蒲澤、團子新居及び龍地に限る。)、韮崎市、北杜市 」のように、カッコの中に「、」があるケースにも対応しましょう。」 🧠「 カッコの中に「、」があるケースに対応するために、以下の修正を行いました。

  • 市町村ごとにテキストを分割する際、カッコ内の「、」を無視する正規表現を追加しました。

修正後のコードは以下の通りです。

import re

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []

    # 都道府県ごとにテキストを分割
    segments_by_pref = re.split(f'(、(?={pref}))', input_text)

    for segment in segments_by_pref:
        for pref in prefectures:
            if pref in segment:
                remaining_text = segment.split(pref, 1)[1]
                break
        else:
            remaining_text = segment

        # カッコ内の「、」を無視して市町村ごとにテキストを分割
        segments = re.split(r'、(?![^(]*))', remaining_text)

    for segment in segments:
        city_or_town = ""
        designated_city = ""
        county = ""
        supplement = ""

        supplement_match = re.search(r'(.*?)', segment)
        if supplement_match:
            supplement = supplement_match.group(0)
            segment = segment.replace(supplement, '')

        for dc in designated_cities:
            if re.fullmatch(dc, segment.strip()):
                designated_city = dc
                segment = segment.replace(dc, '')

        for sp_city in sp_cities:
            if sp_city in segment:
                city_or_town = sp_city
                segment = segment.replace(sp_city, '')
                break

        if not city_or_town:
            match = re.search(r'(.*[市町村])', segment)
            if match:
                city_or_town = match.group(1)
                segment = segment.replace(city_or_town, '')

        match = re.search(r'(.*[郡])', segment)
        if match:
            county = match.group(1)
            segment = segment.replace(county, '')

        result.append([pref, designated_city, city_or_town, supplement])

return result

9回目 実行して出てきたエラーを直させる

👨‍🌾「エラーが発生しました

UnboundLocalError Traceback (most recent call last)

in 82 # 83 for i in range(12): ---> 84 res = parse_location(shigai_list["cities"][i30],prefectures, designated_cities, sp_cities) 85 print("入力:",shigai_list["cities"][i30]," 出力:", res)

in parse_location(input_text, prefectures, designated_cities, sp_cities) 5 6 # 都道府県ごとにテキストを分割 ----> 7 segments_by_pref = re.split(f'(、(?={pref}))', input_text) 8 9 for segment in segments_by_pref:

UnboundLocalError: local variable 'pref' referenced before assignment 」

🧠「すみません。指摘されたエラーが発生しました。都道府県ごとにテキストを分割する際に、正規表現の文字列内で変数prefを参照しているため、エラーが発生しています。以下のようにコードを修正しました。

修正後のコードは以下の通りです。

import re

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []

    # 都道府県ごとにテキストを分割
    segments_by_pref = re.split('|'.join([f'(、(?={pref}))' for pref in prefectures]), input_text)

    for segment in segments_by_pref:
        for pref in prefectures:
            if pref in segment:
                remaining_text = segment.split(pref, 1)[1]
                break
        else:
            remaining_text = segment

        # カッコ内の「、」を無視して市町村ごとにテキストを分割
        segments = re.split(r'、(?![^(]*))', remaining_text)

        for segment in segments:
            city_or_town = ""
            designated_city = ""
            county = ""
            supplement = ""

            supplement_match = re.search(r'(.*?)

10回目 改善が見られない。

改善が見られないので、手元でこの問題はささっと直しました。もう一つエラーが起きたので突きつけます。 英語のほうが通じるのだろうか、と英語で書いてみます。

👨‍🌾「 The following problem is occurring: 郡 is not being processed correctly. ``` 入力: 山形県鶴岡市、東田川郡三川町 出力: [['山形県', '', '鶴岡市', ''], ['山形県', '', '東田川郡三川町', '']] expected: 出力: [['山形県', '', '鶴岡市', ''], ['山形県', '東田川郡','三川町', '']]


The following error also occurs for the input below:

入力: 三重県南牟婁郡紀宝町、和歌山県新宮市、田辺市本宮町、東牟婁郡(北山村、太地町及び那智勝浦町に限る。)

  9     for segment in segments_by_pref:
 10         for pref in prefectures:

---> 11 if pref in segment: 12 remaining_text = segment.split(pref, 1)[1] 13 break

TypeError: argument of type 'NoneType' is not iterable

」
🧠「
失礼しました。ご指摘いただいた問題とエラーを解決するために、以下の修正を行いました。

- 郡が正しく処理されるように、市町村名と郡名が一緒にならないように分割を行うコードを追加しました。
- NoneTypeのエラーが発生しないように、分割後のsegmentをチェックしてから処理を行うように修正しました。

修正後のコードは以下の通りです。

```python
import re

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []

    # 都道府県ごとにテキストを分割
    segments_by_pref = re.split('|'.join([f'(、(?={pref}))' for pref in prefectures]), input_text)

    for segment in segments_by_pref:
        for pref in prefectures:
            if pref in segment:
                remaining_text = segment.split(pref, 1)[1]
                break
        else:
            remaining_text = segment

        # カッコ内の「、」を無視して市町村ごとにテキストを分割
        segments = re.split(r'、(?![^(]*))', remaining_text)

        for segment in segments:
            if not segment:
                continue

            city_or_town = ""
            designated_city = ""
            county = ""
            supplement = ""

            supplement_match = re.search(r'(.*?)', segment)
            if supplement_match:
                supplement = supplement_match.group(0)
                segment = segment.replace(supplement, '')

            for dc in designated_cities:
                if re.fullmatch(dc, segment.strip()):
                    designated_city = dc
                    segment = segment.replace(dc, '')

            for sp_city in sp_cities:
                if sp_city in segment:
                    city_or_town = sp_city
                    segment = segment.replace(sp_city, '')

            for pref in prefectures:
                if re.search(fr'{pref}郡', segment):
                    county = segment.strip()
                    break

            if not city_or_town:
                city_or_town = segment.strip()

            result.append([pref, designated_city, county, city_or_town, supplement])

    return result

上記のコードは、sp_cities内の特別な市町村名を処理し、その後郡名を検索して処理を行い、最後に市町村名がまだ見つかっていない場合は、残りのテキストを市町村名として使用します。これで、郡名が正しく処理されるはずです。 」

11回目 都道府県が全部沖縄県になる問題

ループの結果を保存していないため、都道府県がかならず配列の最後の沖縄県になる問題が発生しました。

👨‍🌾「全部沖縄県になってしまいます

入力: 北海道江別市、札幌市、北広島市、空知郡南幌町 出力: [['沖縄県', '', '', '江別市', ''], ['沖縄県', '札幌市', '', '', ''], ['沖縄県', '', '', '北広島市', ''], ['沖縄県', '', '', '空知郡南幌町', '']]」

🧠「おっしゃる通り、間違った出力が得られてしまっています。問題を修正するために、都道府県名の検索と処理部分を変更しました。修正後のコードは以下の通りです。

import re

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []

    # 都道府県ごとにテキストを分割
    segments_by_pref = re.split('|'.join([f'(、(?={pref}))' for pref in prefectures]), input_text)

    current_pref = ''
    for segment in segments_by_pref:
        for pref in prefectures:
            if pref in segment:
                current_pref = pref
                remaining_text = segment.split(pref, 1)[1]
                break
        else:
            remaining_text = segment

        # カッコ内の「、」を無視して市町村ごとにテキストを分割
        segments = re.split(r'、(?![^(]*))', remaining_text)

        for segment in segments:
            if not segment:
                continue

            city_or_town = ""
            designated_city = ""
            county = ""
            supplement = ""

            supplement_match =

」 やりたいことはわかったので、この部分だけで手元のコードを修正

12回目 複数の都道府県を含む文字列でのエラー

複数の都道府県を含む文字列でのエラーが出てしまいます。この部分だけ抜き出して聞いてみましょう。

👨‍🌾「I wrote the (code) and I wanted (expected) but I got (output). how should do i fix it?

(code)""" prefectures = [ '北海道', '大阪府', '三重県', '和歌山県', '鹿児島県', '沖縄県' ]

re.split( '|'.join([f'(、(?={pref}))' for pref in prefectures]), '三重県南牟婁郡紀宝町、和歌山県新宮市、田辺市本宮町' ) """ (output)""" ['三重県南牟婁郡紀宝町', None, None, None, '、', None, None, '和歌山県新宮市、田辺市本宮町'] """ (expected)""" ['三重県南牟婁郡紀宝町', '和歌山県新宮市、田辺市本宮町'] """」

🧠「 問題は、re.split()が正規表現にマッチした部分を含めてリストに入れてしまっているためです。この問題を解決するために、正規表現のパターンを変更し、マッチした部分を含まないようにします。

以下のように修正してみてください。

import re

prefectures = [
    '北海道', '大阪府', '三重県', '和歌山県', '鹿児島県', '沖縄県'
]

output = re.split(
    f'(?<=県)(?=、(?:{"|".join(prefectures)}))',
    '三重県南牟婁郡紀宝町、和歌山県新宮市、田辺市本宮町'
)

print(output)

どうにもならなかったので、自分でコードを書きました。ヨシ!

    segments_by_pref = re.split('|'.join([f'(、(?={pref}))' for pref in prefectures]), input_text)
    segments_by_pref = [e for e in segments_by_pref if e is not None and e != '、'] #  Noneと「、」は除去する

13回目 入れ子のカッコをなんとかして

入れ子のカッコ(千葉市(花見川区(柏井、柏井町及び横戸町に限る。)を除く。)のような)を正規表現でどうにかするのは難しいんですが、無茶を言ったら通りました。

素直にすごいと思います。

👨‍🌾「 入力「千葉県佐倉市、千葉市(花見川区(柏井、柏井町及び横戸町に限る。)を除く。)、八街市、四街道市、印旛郡酒々井町」に対して、出力「[['千葉県', '', '佐倉市', ''], ['千葉県', '千葉市', '',(花見川区(柏井、柏井町及び横戸町に限る。)を除く。)'], ['千葉県', '', '八街市', ''], ['千葉県', '', '四街道市', ''], ['千葉県', '印旛郡', '酒々井町', '']]」が欲しいです。入れ子のカッコであってもうまく外側のカッコの組み合わせ及びその中身を抽出できる方法を考えてください。 」 🧠「 正規表現を使って入れ子のカッコを処理するのは難しいため、代わりにカッコ内のテキストを一時的に置き換え、その後処理を行い、最後に置き換えたテキストを元に戻す方法を提案します。

以下のコードは、そのアプローチを取り入れたものです。

def parse_location(input_text, prefectures, designated_cities, sp_cities):
    result = []

    # カッコ内のテキストを一時的に置き換え
    brackets = re.findall(r'([^)]*)', input_text)
    for i, bracket in enumerate(brackets):
        input_text = input_text.replace(bracket, f"{{{i}}}")

    # 都道府県ごとにテキストを分割
    segments_by_pref = re.split(f'(、(?={"|".join(prefectures)}))', input_text)

    current_pref = ''
    for segment in segments_by_pref:
        for pref in prefectures:
            if pref in segment:
                current_pref = pref
                remaining_text = segment.split
                remaining_text = segment.split(pref, 1)[1]
                break
        else:
            remaining_text = segment

        # カッコ内の「、」を無視して市町村ごとにテキストを分割
        segments = re.split(r'、(?![^{]*})', remaining_text)

        for segment in segments:
            if not segment:
                continue

            city_or_town = ""
            designated_city = ""
            county = ""
            supplement = ""

            # カッコ内のテキストを元に戻す
            for i, bracket in enumerate(brackets):
                segment = segment.replace(f"{{{i}}}", bracket)

            supplement_match = re.search(r'([^)]*)', segment)
            if supplement_match:
                supplement = supplement_match.group()
                segment = segment.replace(supplement, '')

            for dc in designated_cities:
                if dc in segment:
                    designated_city = dc
                    segment = segment.replace(dc, '')

これ以上気になった点はさっと直して、とりあえず完成

まだ改良はしますが、ひとまず完成しました。

import re

def parse_location(
    input_text,
    prefectures,
    designated_cities,
    sp_counties,
    sp_cities ):
    result = []

    # カッコ内のテキストを一時的に置き換え
    brackets = re.findall(r'([^)]*)', input_text)
    for i, bracket in enumerate(brackets):
        input_text = input_text.replace(bracket, f"{{{i}}}")
    
    # 都道府県ごとにテキストを分割
    segments_by_pref = re.split('|'.join([f'(、(?={pref}))' for pref in prefectures]), input_text)
    segments_by_pref = [e for e in segments_by_pref if e is not None and e != '、']

    current_pref = ''
    for segment in segments_by_pref:
        for pref in prefectures:
            if pref in segment:
                current_pref = pref
                remaining_text = segment.split(pref, 1)[1]
                break
        else:
            remaining_text = segment
        # カッコ内の「、」を無視して市町村ごとにテキストを分割
        segments = re.split(r'、(?![^{]*})', remaining_text)

        for segment in segments:
            if not segment:
                continue

            city_or_town = ''
            designated_city = ''
            county = ''
            supplement = ''
            
            # 置き換えたカッコ内のテキストを元に戻す
            segment = segment.format(*brackets)

            supplement_match = re.search(r'(.*)', segment)
            if supplement_match:
                supplement = supplement_match.group(0)
                segment = segment.replace(supplement, '')

            for dc in designated_cities:
                if segment.strip().startswith(dc):
                    designated_city = dc
                    segment = segment.replace(dc, '')

            for sp_city in sp_cities:
                if sp_city in segment:
                    city_or_town = sp_city
                    segment = segment.replace(sp_city, '')

            for sp_county in sp_counties:
                if sp_county in segment:
                    county = sp_county
                    segment = segment.replace(sp_county, '')


            county_index = segment.find('郡')
            if county_index > 0:
                county = segment[0:county_index+1]
                segment = segment.replace(county, '')

            if not city_or_town:
                for delimiter in ['市', '町', '区']:
                    index = segment.find(delimiter)
                    if index > 0:
                        city_or_town = segment[0:index+1]
                        segment = segment.replace(city_or_town, '')
                        break

            result.append([
                current_pref,
                designated_city + county,
                city_or_town,
                segment + supplement
            ])

    return result




# 事前に定義されている配列
# 都道府県
prefectures = [
    '北海道', '青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県', '茨城県', '栃木県', '群馬県',
    '埼玉県', '千葉県', '東京都', '神奈川県', '新潟県', '富山県', '石川県', '福井県', '山梨県', '長野県',
    '岐阜県', '静岡県', '愛知県', '三重県', '滋賀県', '京都府', '大阪府', '兵庫県', '奈良県', '和歌山県',
    '鳥取県', '島根県', '岡山県', '広島県', '山口県', '徳島県', '香川県', '愛媛県', '高知県', '福岡県',
    '佐賀県', '長崎県', '熊本県', '大分県', '宮崎県', '鹿児島県', '沖縄県'
]

# 自治体名に市や郡を含むので先に処理をしたほうがよい自治体

sp_counties = [
    '余市郡', '高市郡',  '東村山郡', '西村山郡', '北村山郡', '田村郡'
]

sp_cities = [  
    '市川市', '市原市', '野々市市', '四日市市', '廿日市市', '町田市', '十日町市', '大町市', '村山市', 
    '田村市', '東村山市', '武蔵村山市', '羽村市', '村上市', '大村市', '郡山市', '郡上市', '蒲郡市',  
    '大和郡山市', '小郡市', 
    '市川三郷町', '市川町',  '余市町', '市貝町', '上市町', '下市町', '大町町', '村田町', '玉村町', '上郡町', '中村区'
]

# 政令指定都市
designated_cities = [
    '札幌市', '仙台市', 'さいたま市', '千葉市', '横浜市', '川崎市', '相模原市', '新潟市',
    '静岡市', '浜松市', '名古屋市', '京都市', '大阪市', '堺市', '神戸市', '岡山市', '広島市', '北九州市', '福岡市', '熊本市'
]


#
for i in range(120):
  res = parse_location(shigai_list["cities"][i*3],prefectures, designated_cities, sp_counties, sp_cities)
  print("入力:",shigai_list["cities"][i*3],"  出力:", res)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment