【ちょうorまち】「町」がつく駅名をBeautifulSoupで取得、foliumで位置を図示化してみた

日本の自治体における「○○町」が「ちょう」と読むか「まち」と読むかに関する記事を見かけました。
<a  href="https://www.fnn.jp/articles/-/216519" target="_blank" rel="noopener noreferrer">“町”は「まち」か「ちょう」か…調査して分かった東日本・西日本での傾向　境界線らしき川が長野にあった</a>（最終閲覧日：2021年8月6日）
![map_station4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182283/91f7f780-bc56-028f-afc1-11f9f0a1e905.jpeg)

長野あたりに境界ができるのは面白いですね。

私はこの記事を読んで、駅名でも「ちょう」と読むか「まち」と読むかの境界線があるかどうか気になりました。そこで、データの取得（スクレイピング）、簡単なデータ分析、foliumによる位置情報の図示化をしてみました。



<h1>データ取得</h1>
データは以下のWikipediaのリンクから出発して、BeautifulSoupでスクレイピングしました。
<a  href="https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E9%89%84%E9%81%93%E9%A7%85%E4%B8%80%E8%A6%A7" target="_blank" rel="noopener noreferrer">日本の鉄道駅一覧 - Wikipedia</a>

今回取得および作成したデータは以下の通りです：

- 名称に「町」のつく駅の駅名（先頭が町の駅も含む）
- 駅名の仮名
- 「ちょう」か「まち」かを判別するラベル
- 都道府県
- 緯度
- 経度

```python
from bs4 import BeautifulSoup
import requests
import re
import time

url = "https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E9%89%84%E9%81%93%E9%A7%85%E4%B8%80%E8%A6%A7"
res = requests.get(url)
soup = BeautifulSoup(res.content,'html.parser')
# 50音のリンクを取得
list_url_aiueo = ["https://ja.wikipedia.org"+x.get("href") for x in soup.select('td>a') if "日本の鉄道駅一覧" in x.get("title")]
list_url_all = []
list_name_all = []
list_namekana_all = []

# 50音のリンクから駅名に「町」を含む駅を取得
for url in list_url_aiueo:
    print(url)
    res = requests.get(url)
    soup = BeautifulSoup(res.content,'html.parser')
    list_name = [x.text for x in soup.select('li>a') if "町" in re.sub(r"\(.+?\)$", "", x.text)]
    list_namekana = [x.parent.text.replace(x.text, "")[1:-1] for x in soup.select('li>a') if "町" in re.sub(r"\(.+?\)$", "", x.text)]
    list_url = ["https://ja.wikipedia.org"+x.get("href") for x in soup.select('li>a') if "町" in re.sub(r"\(.+?\)$", "", x.text)]
    
    list_name_all.extend(list_name)
    list_namekana_all.extend(list_namekana)
    list_url_all.extend(list_url)
    #2秒待つ
    time.sleep(2)

data = []
for n in range(len(list_url_all)):
    d = []
    name = list_name_all[n]
    print(name)
    name_kana = list_namekana_all[n]
    url = list_url_all[n]
    print(url)
    res = requests.get(url)
    soup = BeautifulSoup(res.content,'html.parser')
    
    #文字列 name_kana 内に…
    #"ちょう"も"まち"も含む → 0
    #"ちょう"を含むが、"まち"は含まない → 1
    #"ちょう"を含まないが、"まち"は含む → 2
    #"ちょう"も"まち"も含まない（そんな駅はなさそうだが一応）→3
    if "ちょう" in name_kana:
        if "まち" in name_kana:
            tyou_machi = 0
        else:
            tyou_machi = 1
    else:
        if "まち" in name_kana:
            tyou_machi = 2
        else:
            tyou_machi = 3
    
    #wikiの本文は「○○駅は、××県にある△△線の駅である」で始まることが予想される
    #→pタグ内の最初のaタグを取得すれば都道府県が取得できそう（あとで要検証）
    #「○○停留場」とあるのにリンク先は「○○駅」になっていたものもあったので、文字列一致条件から除外
    nm = re.sub(r"\(.+?\)$", "", name).strip().replace("停留場","")
    if [x.text for x in soup.select('p>a') if x.parent.text[:len(nm)]==nm] == []:
        pref = None
    else:
        pref = [x.text for x in soup.select('p>a') if x.parent.text[:len(nm)]==nm][0]
    
    time.sleep(2)
    list_href = [x.get("href") for x in soup.select("a") if x.get("href") != None]
    if [x for x in list_href if "geohack" in x] == []:
        latitude = None
        longitude = None
    else:
        url_map = [x for x in list_href if "geohack" in x][0]
        print(url_map)
        res_map = requests.get(url_map)
        soup_map = BeautifulSoup(res_map.content,'html.parser')
        latitude = soup_map.select_one('span[class^="latitude p-latitude"]').text
        longitude = soup_map.select_one('span[class^="longitude p-longitude"]').text

    d = [name, name_kana, tyou_machi, pref, latitude, longitude]
    data.append(d)
    time.sleep(2)
print("end")
```

上記で取得したcsvをpandasで読み込み、正しく取得できていない都道府県データを確認します。


```python
import pandas as pd
df = pd.DataFrame(data, columns=["駅名", "かな", "ちょう1まち2", "都道府県", "緯度", "経度"])

#緯度経度をfloat型にする
df["緯度"].astype(float)
df["経度"].astype(float)

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

#pref_listに載ってないデータを見る
df.query(f"都道府県 not in {pref_list}")
```

```python
	駅名	かな	ちょう1まち2	都道府県	緯度	経度
41	恵美須町駅	えびすちょうえき・Osaka Metro堺筋線	1	阪堺電気軌道	34.653867	135.504878
42	恵美須町停留場	えびすちょうていりゅうじょう・阪堺電気軌道阪堺線	1	阪堺電気軌道	34.653867	135.504878
44	猿猴橋町停留場	えんこうばしちょうていりゅうじょう	1	広島市	34.394794	132.476033
96	紙屋町西停留場	かみやちょうにしていりゅうじょう	1	None	34.39525	132.457667
97	紙屋町東停留場	かみやちょうひがしていりゅうじょう	1	None	34.39525	132.457667
108	観音町停留場	かんおんまちていりゅうじょう・広島電鉄本線	2	広島市	34.395503	132.438578
123	小網町停留場	こあみちょうていりゅうじょう	1	広島市	34.394456	132.44435
151	桜木町駅 (YOKOHAMA AIR CABIN)	さくらぎちょうえき・泉陽興業YOKOHAMA AIR CABIN	1	None	35.451389	139.631472
192	陣屋町臨港駅	じんやまちりんこうえき	2	None	42.367875	140.949989
244	伝馬町駅	てんまちょうえき・名古屋市営地下鉄名城線	0	名古屋市	35.120833	136.910556
245	天満町停留場	てんまちょうていりゅうじょう・広島電鉄本線	0	広島市	34.3951	132.440878
246	十日市町停留場	とうかいちまちていりゅうじょう	2	広島市	34.39735	132.44765
266	西観音町停留場	にしかんおんまちていりゅうじょう	2	広島市	34.394061	132.436672
287	浜町アーケード停留場	はまのまちアーケードていりゅうじょう	2	None	32.743764	129.876081
303	福島町停留場	ふくしまちょうていりゅうじょう	0	広島市	34.394875	132.433975
305	袋町停留場	ふくろまちていりゅうじょう	2	広島市	34.391183	132.456211
313	舟入川口町停留場	ふないりかわぐちちょうていりゅうじょう	1	広島市	34.380453	132.439639
315	舟入本町停留場	ふないりほんまちていりゅうじょう	2	広島市	34.385714	132.442714
316	舟入町停留場	ふないりまちていりゅうじょう	2	広島市	34.389403	132.444978
326	本川町停留場	ほんかわちょうていりゅうじょう	1	広島市	34.396839	132.450483
328	本町一丁目停留場	ほんまちいっちょうめていりゅうじょう	0	None	33.840139	132.758339
336	町屋駅前停留場	まちやえきまえていりゅうじょう	2	None	35.742222	139.781389
360	皆実町六丁目停留場	みなみまちろくちょうめていりゅうじょう	0	広島市	34.3742	132.464058
370	モノレール浜松町駅	モノレールはままつちょうえき	1	None	35.655	139.756944
```

データを以下のように修正します。

```python
#一括修正
df.loc[df["都道府県"]=="広島市", "都道府県"] = "広島県"
df.loc[df["都道府県"]=="阪堺電気軌道", "都道府県"] = "大阪府"
df.loc[df["都道府県"]=="名古屋市", "都道府県"] = "愛知県"
#nullを埋める
df.loc[df["駅名"]=="紙屋町西停留場", "都道府県"] = "広島県"
df.loc[df["駅名"]=="紙屋町東停留場", "都道府県"] = "広島県"
df.loc[df["駅名"]=="桜木町駅 (YOKOHAMA AIR CABIN)", "都道府県"] = "神奈川県"
df.loc[df["駅名"]=="陣屋町臨港駅", "都道府県"] = "北海道"
df.loc[df["駅名"]=="浜町アーケード停留場", "都道府県"] = "長崎県"
df.loc[df["駅名"]=="本町一丁目停留場", "都道府県"] = "愛媛県"
df.loc[df["駅名"]=="町屋駅前停留場", "都道府県"] = "東京都"
df.loc[df["駅名"]=="モノレール浜松町駅", "都道府県"] = "東京都"
```

次にラベルに関するデータを確認します。

```python
df[(df["ちょう1まち2"]==0)|(df["ちょう1まち2"]==3)]
```

```python
駅名	かな	ちょう1まち2	都道府県	緯度	経度
3	青山町駅	あおやまちょうえき・近鉄大阪線	0	三重県	34.672847	136.177764
5	曙町東町停留場	あけぼのちょうひがしまちていりゅうじょう	0	高知県	33.551575	133.493408
7	旭町一丁目停留場	あさひまちいっちょうめていりゅうじょう	0	高知県	33.556742	133.513814
8	旭町三丁目停留場	あさひまちさんちょうめていりゅうじょう	0	高知県	33.555722	133.506514
19	伊勢佐木長者町駅	いせざきちょうじゃまちえき	0	神奈川県	35.441083	139.633139
32	浮島町駅	うきしまちょうえき	0	神奈川県	35.522944	139.782944
81	勝山町停留場	かつやまちょうていりゅうじょう	0	愛媛県	33.841681	132.774867
85	銀山町停留場	かなやまちょうていりゅうじょう	0	広島県	34.393247	132.466906
91	上町一丁目停留場	かみまちいっちょうめていりゅうじょう	0	高知県	33.557411	133.525464
92	上町五丁目停留場	かみまちごちょうめていりゅうじょう	0	高知県	33.557253	133.517064
93	上町二丁目停留場	かみまちにちょうめていりゅうじょう	0	高知県	33.557406	133.523358
94	上町四丁目停留場	かみまちよんちょうめていりゅうじょう	0	高知県	33.557369	133.520067
100	萱町六丁目停留場	かやまちろくちょうめていりゅうじょう	0	愛媛県	33.8512	132.756297
101	辛島町停留場	からしまちょうていりゅうじょう	0	熊本県	32.799014	130.704958
128	小伝馬町駅	こでんまちょうえき	0	東京都	35.69075	139.778472
213	高島町駅	たかしまちょうえき	0	神奈川県	35.458583	139.62375
218	谷町九丁目駅	たにまちきゅうちょうめえき	0	大阪府	34.667144	135.515842
219	谷町四丁目駅	たにまちよんちょうめえき	0	大阪府	34.681914	135.517286
220	谷町六丁目駅	たにまちろくちょうめえき	0	大阪府	34.674917	135.516958
232	長者町駅	ちょうじゃまちえき	0	千葉県	35.295861	140.385
244	伝馬町駅	てんまちょうえき・名古屋市営地下鉄名城線	0	愛知県	35.120833	136.910556
245	天満町停留場	てんまちょうていりゅうじょう・広島電鉄本線	0	広島県	34.3951	132.440878
260	長町一丁目駅	ながまちいっちょうめえき	0	宮城県	38.234228	140.887486
262	中町（西町北）停留場	なかまち（にしちょうきた）ていりゅうじょう	0	富山県	36.690306	137.215722
265	西辛島町停留場	にしからしまちょうていりゅうじょう	0	熊本県	32.798639	130.701444
286	浜町駅	はまちょうえき	0	東京都	35.688472	139.788139
290	播磨町駅	はりまちょうえき	0	兵庫県	34.716561	134.8681
303	福島町停留場	ふくしまちょうていりゅうじょう	0	広島県	34.394875	132.433975
328	本町一丁目停留場	ほんまちいっちょうめていりゅうじょう	0	愛媛県	33.840139	132.758339
329	本町五丁目停留場	ほんまちごちょうめていりゅうじょう	0	愛媛県	33.850653	132.758836
330	本町三丁目停留場	ほんまちさんちょうめていりゅうじょう	0	愛媛県	33.845158	132.758825
331	本町四丁目停留場	ほんまちよんちょうめていりゅうじょう	0	愛媛県	33.847956	132.758822
332	本町六丁目停留場	ほんまちろくちょうめていりゅうじょう	0	愛媛県	33.853322	132.758892
337	町屋二丁目停留場	まちやにちょうめていりゅうじょう	0	東京都	35.743694	139.776944
359	皆実町二丁目停留場	みなみまちにちょうめていりゅうじょう	0	広島県	34.377836	132.466103
360	皆実町六丁目停留場	みなみまちろくちょうめていりゅうじょう	0	広島県	34.3742	132.464058
```

（さすがに「町」を「ちょう」「まち」以外で読む駅はないようですね）

それでは、ラベルを以下のように修正します。

```python
#データを見ると、青山町（あおやまちょう）などのように"○○まちょう"は、"まち"も"ちょう"も含む判定になっている
#→"ちょう"判定に修正
df.loc[df["かな"].str.contains("まちょう"), "ちょう1まち2"] = 1
#"丁"を含み、「ちょう1まち2」が0のデータは"まち"
df.loc[(df["駅名"].str.contains("丁"))&(df["ちょう1まち2"]==0), "ちょう1まち2"] = 2
#ちょうじゃまち駅なので"まち"
df.loc[df["駅名"]=="伊勢佐木長者町駅", "ちょう1まち2"] = 2
df.loc[df["駅名"]=="長者町駅", "ちょう1まち2"] = 2

df[(df["ちょう1まち2"]==0)|(df["ちょう1まち2"]==3)]
```

```python
	駅名	かな	ちょう1まち2	都道府県	緯度	経度
5	曙町東町停留場	あけぼのちょうひがしまちていりゅうじょう	0	高知県	33.551575	133.493408
262	中町（西町北）停留場	なかまち（にしちょうきた）ていりゅうじょう	0	富山県	36.690306	137.215722
```
「町」を2つ含んで、それぞれ別の読み方をする駅が2つあるんですね…。この2つは例外的に除くことにします。

<h1>「ちょう」と「まち」どちらが多い？</h1>
とりあえず「ちょう」読むか「まち」と読むかどちらが多いかだけでも確認してみます。

```python
df = df[df["ちょう1まち2"]!=0] #曙町東町停留場と中町（西町北）停留場を除いた
print(f'「町」のつく駅の総数: {len(df)}')
print(f'「ちょう」と読む駅の数: {len(df[df["ちょう1まち2"]==1])}')
print(f'「まち」と読む駅の数: {len(df[df["ちょう1まち2"]==2])}')
print(f'「ちょう」と読む駅の割合: {len(df[df["ちょう1まち2"]==1])/len(df)}')
print(f'「まち」と読む駅の割合: {len(df[df["ちょう1まち2"]==2])/len(df)}')
```

```python
「町」のつく駅の総数: 387
「ちょう」と読む駅の数: 158
「まち」と読む駅の数: 229
「ちょう」と読む駅の割合: 0.4082687338501292
「まち」と読む駅の割合: 0.5917312661498708
```

「ちょう」は約4割、「まち」が6割のようです。ちなみに「まち」と読む割合が7割をこえる都道府県は以下の通りです。

|都道府県|町のつく駅|まちと読む駅|まちと読む割合|
|---|---|---|---|
|宮城県|	12|10|0.833333|
|山形県|	1|1|1.000000|
|福島県|	5|5|1.000000|
|群馬県|	5|5|1.000000|
|埼玉県|	2|2|1.000000|
|新潟県|	6|6|1.000000|
|富山県|	21|15|0.714286|
|石川県|	1|1|1.000000|
|福井県|	4|4|1.000000|
|長野県|	9|7|0.777778|
|三重県|	7|5|0.714286|
|滋賀県|	2|2|1.000000|
|京都府|	7|6|0.857143|
|兵庫県|	10|8|0.800000|
|島根県|	2|2|1.000000|
|愛媛県|	17|12|0.705882|
|福岡県|	5|5|1.000000|
|佐賀県|	1|1|1.000000|
|長崎県|	14|14|1.000000|
|大分県|	1|1|1.000000|
|宮崎県|	2|2|1.000000|

『100%「まち」と読む県』が九州に多いようです。


<h1>foliumで位置の図示化</h1>
それではfoliumを使って位置を図示化してみます。

```python
map_station = folium.Map(location=[36.000385797, 137.999954223], zoom_start=5)
 
for i, r in df[df["ちょう1まち2"]==1].iterrows():
    folium.Marker(
        location=[r["緯度"], r["経度"]],
        popup=r["駅名"],
        icon=folium.Icon(color='red')
    ).add_to(map_station)

for i, r in df[df["ちょう1まち2"]==2].iterrows():
    folium.Marker(
        location=[r["緯度"], r["経度"]],
        popup=r["駅名"],
        icon=folium.Icon(color='blue')
    ).add_to(map_station)

# htmlとして保存する場合は以下を実行
map_station.save("map_station.html")

map_station
```

![map_station.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182283/060e6f4a-1223-b40b-2dff-b5870d535e4b.jpeg)

![map_station2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182283/c465c85a-314d-8d91-7570-0d1fd13f91d0.jpeg)

![map_station3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182283/24a9d278-997f-62df-0476-456e6c24dbb4.jpeg)

自治体の「ちょう」「まち」のときように、きれいな境界線は無いようですね。このままだとSVMのような機械学習で判別するのは難しそうです。ちなみに、大阪付近では何となく「ちょう」「まち」の境界らしきものは見つかりました。県単位で「ちょう」「まち」判別する方法があるかもしれません。


<h1>まとめと今後</h1>
「町」がつく駅名のデータを取得し、foliumで「町」がつく駅名の位置を図示化してみました。もう少し時間があれば他のデータ等を利用して、よさげな「ちょう」「まち」の2値判別器を検討してみたいと思います。