OpenRefineで住所をパースする


# 動機

住所が与えられていて、それから自治体を抽出したいということはよくあると思う。でも住所のパースはそんなに簡単な作業ではないし、そこに誤記が含まれていたりするとかなり大変。ここではOpenRefineを使ってwikidata上の情報を参照しながらパースしていく手順を、覚え書きとして残しておく。これが決定版というわけではないけれど、使っている正規表現はOpenRefineではなくスクリプトとかでパースする場合にも参考になるかもしれない。

# 目的
都道府県から始まる住所から基礎自治体（市町村＋特別区）[^2]を抽出し、wikidataのIDにマッピングする。
[^2]: 2022年12月時点で存在する基礎自治体が対象。過去の自治体もある程度は扱えるが細かいところで不具合が出てくると思われる。

:::note info
今回はwikidataへのReconciliationを目的としているためかなり煩雑になっているが、誤字の検出やIDの対応付けが不要な場合にはReconcileする必要はなく、正規表現による切り出し4回でパース自体はできる。
:::

# 注意事項
* wikidataのデータが書き換わることで上手く処理できなくなる可能性がある
* 一般論として都道府県は一意な文字列になるが、市町村は一意な文字列にならない。たとえば「府中市」には東京都府中市と広島県府中市があるので、都道府県と組み合わせた状態でなければこの2つを区別できない（[同一名称の市区町村一覧](https://ja.wikipedia.org/wiki/%E5%90%8C%E4%B8%80%E5%90%8D%E7%A7%B0%E3%81%AE%E5%B8%82%E5%8C%BA%E7%94%BA%E6%9D%91%E4%B8%80%E8%A6%A7)を参照）[^1]。この手順ではwikidataに対してReconciliationすることで別のIDとして区別されるようになっている。
[^1]: マニアックな話だが、北海道には旧国名で区別されうる（＝都道府県との組み合わせでは区別できない）同一名称の郡（上川郡と中川郡）が存在している。この手順では一応区別して扱っている。

# 処理の流れ
1. 都道府県を抽出する
1. 郡を抽出する
1. 郡部にある町村を抽出する
1. 郡部以外にある市区町村[^3]を抽出する
[^3]: 東京都の島嶼部には郡に属さない町村がある

# 手順
実例として[文部科学省学校コード](https://www.mext.go.jp/b_menu/toukei/mext_01087.html)の[令和4年暫定版CSVファイル（東日本）](https://www.mext.go.jp/content/220908-mxt-mxt_chousa01-1000011635_2.csv)を用いる。東日本だけになるが津々浦々の学校の住所が羅列されているデータセットである[^4]。最初の行にメタデータ（といってもタイトルと更新日だけ）が入っているので1行無視して読み込む。
[^4]: 作成の都合上、最低限のカラムに絞り込んで作図しているが、本当はもう少し色々な情報が入っている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/800e8804-3db4-9960-e379-4980cf5e0d30.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/6626d5ed-1ced-0a37-a1ab-51b51ddb2fb1.png)


## 都道府県
"Add column based on this column"で都道府県名を切り出す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/f2bf4c0d-8a3e-eb71-ebec-9835d69e666e.png)

カラム名は`prefLabel`として以下のGREL式を使う。都道府県名が省略されているなどでマッチしない場合はnullになる。

```
value.find(/^(.{2}[都道府県]|.{3}県)/)[0]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/ebb07e3b-866c-55c0-ce50-89754437d6e3.png)


:::note warn
ここで`.{2,3}[都道府県]`とまとめてしまうと、山梨県**都**留市、広島県**府**中市などで不正なパースになる。
:::

後で使うのでprefLabelカラムをQ50337 (prefecture of Japan)に対してReconcileしておく。

:::note info
パースさえできれば十分で、誤字の検出やIDへの対応付けが不要な場合にはReconcileする必要はない。以下同じ。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/9cf2e9a0-1d48-1a41-465c-822159b1d832.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/d0b63eb5-b755-08f8-0761-86be1d4fdb66.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/3c8505b0-c1af-9f91-f857-4ed572877265.png)

ここでマッチしないものがある場合、都道府県名の省略・誤字や旧自治体名などが原因なので個別に修正が必要。

## 郡

同様に以下の式を使い郡名をgunLabelカラムに切り出す。

```
value.replace(cells.prefLabel.value,"").find(/^(中郡|.[^和]郡|[^市区]{3,4}郡)/)[0]
```

:::note warn
蒲**郡**市・小**郡**市、大和**郡**山市、丸亀市**郡**家町などを正しくパースするために3分割している。これはそれぞれ、1字+郡は「中郡」のみ、2字+郡は2字目に「和」が存在しない、郡名に「市」や「区」は含まれない、という知識を利用している。このあたりは自治体合併がある度にチェックが必要。
:::

Facet by blankを設定しfalseを選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/82e63cd7-d4c6-1d60-c6d5-55c9e5addade.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/81460673-20c2-9323-c8f4-4d9395e341a1.png)

Facetでfalseを選択した状態でgunLabelカラムをQ1122846 (district of Japan)に対してReconcileする。このときprefLabelカラムをプロパティP131として含める。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/64cc602a-f775-dc96-561a-365681c91dde.png)

:::note warn
含めないと愛知県海部郡と徳島県海部郡のような同字郡を区別できず、以下の確認作業に余計な手間がかかる。
:::

wikidata上のデータ構造の関係で、郡名へのReconciliationは一意に定まらないことが多く、"gunLabel: judgment"のFacetでnoneを選択した状態で個別に選んでいく必要がある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/b57afd15-fbc4-1e70-a464-8c8e6c24e306.png)


* 北海道では郡と道の間に総合振興局が挟まっている場合が多く、そのような郡名は完全マッチしない。
* * 通常はトップヒット（スコア100）のものを選べば良い
* * しかし上川郡と中川郡は複数アイテム存在しており、手作業でチェックが必要。
　上川郡は
　Q1134139（十勝国・East area）が新得町・清水町の２つ
　Q1185437 (天塩国・North area）が和寒町・剣淵町・下川町の３つ
　あとはQ1185413（石狩国・South area）なので、
　Text filter (regexp)で選んでMatch allしていく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/f21a1dc1-0ee4-c95a-9aab-b11ab6c4efa4.png)
　中川郡は同様に
　Q690865（上川総合振興局）が美深町・音威子府村・中川町の３つ
　Q1134132（十勝総合振興局）が幕別町・池田町・豊頃町・本別町の４つ
* 島根県隠岐郡も隠岐支庁のせいでトップヒットにならないので注意

作業を終えたら"gunLabel: judgment"のFacetを解除する。

## 郡部の町村

※東京都島嶼部の町村は郡がないのでここでは扱わない

Facetでfalseを選択した状態で以下の式を用いてcityLabelカラムに切り出す。

```
value.replace(cells.prefLabel.value,"").replace(cells.gunLabel.value,"").find(/^([^大玉][町村]|[大玉][町村]町|.[^町村]{1,4}[町村])/)[0]
```

:::note warn
大**町**町および玉**村**町を正しくパースするため正規表現を3分割している
:::

これまで同様にcityLabelカラムをQ1054813 (municipality of Japan)に対してReconcileする。
このときgunLabelカラムをプロパティP131として含める。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/f93ae0d9-c7de-cecf-76d3-cb208b458f64.png)

郡名同様、wikidata上のデータ構造の関係で、町村名へのReconciliationは一意に定まらないことがある。"cityLabel: judgment"のFacetでnoneを選択した状態で個別に選んでいく必要がある。

* とくに真狩村・釧路町は正解がトップヒットではないので注意が必要

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/98cca88c-176a-f5a4-4b00-51709191cc7b.png)

作業を終えたら"cityLabel: judgment"のFacetを解除する。

## 郡部以外の市区町村

※東京都島嶼部の町村は郡がないのでこちらで扱う

gunLabelのFacetでtrueを選択した状態で以下の式を用いてcityLabelカラムに切り出す。すでにcityLabelカラムは存在しているので、"Transform"を使う方が手数が少なくて済む。

```
cells.学校所在地.value.replace(cells.prefLabel.value,"").find(/^(市[川原]市|[四廿野][日々]市市|[^市区]{1,6}市|[北港]区|[^市区町村]{2,3}[区町村])/)[0]
```
:::note warn
前半は**市**川市・**市**原市・四日**市**市・廿日**市**市・野々**市**市への対策込みの市、後半は北区・港区への対策込みの東京都特別区および島嶼部[^5]
:::
[^5]: 島嶼部については対応が甘いかもしれない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/7daec4b4-beb6-3d5e-aee2-ea17aad12038.png)

同様にcityLabelカラムをQ1054813 (municipality of Japan)に対してReconcileする。
このときprefLabelカラムをプロパティP131として含める。

とくに北海道や東京都島嶼部で顕著だが、Reconciliationで一意に定まらないことがあるのもこれまでと同様である。

# 結果
ずいぶん煩雑だと感じる人が多いのではないかと思うが、Reconciliationはせずに正規表現で抽出するだけならかなり便利に使える。Reconciliationのところは、wikidataではなく自前で専用のデータセットを用意しておけばぐっと簡便になるはずである。

今回の実例では33,308件中最終的に10件について自治体へのマッピングができなかった。いずれも東京都島嶼部で、全国的にみると一般的ではない表記（○○郡に相当する場所が○○島となっている）が使われていたためである。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/57d74b40-0a66-a876-cdc8-065e1c15403b.png)

なお同様に西日本のデータセット（[令和4年暫定版CSVファイル（西日本）](https://www.mext.go.jp/content/220908-mxt-mxt_chousa01-1000011635_5.csv)）を用いると、23,631件のうち80件がマッピング不能で、うち78件が「宝**塚**市」が文字化けしたことによるもの、あとは「岡山県岡山**県**」と「愛媛県今**冶**市」という誤字によるものであった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253516/56db667e-80e3-b6e0-3480-46af17f99c0f.png)