郵便番号CSV

[2023-06-30] 日本郵便の郵便番号データダウンロードに「1レコード1行、UTF-8形式」が追加された。これでKEN_ALLに悩まされずに済むかも。

郵便番号データは日本郵便の郵便番号データダウンロードからダウンロードできる。通常は読み仮名データの促音・拗音を小書きで表記するもの(zip形式)にある「全国一括」をクリックして ken_all.zip をダウンロードし,展開して KEN_ALL.CSV を得る。

しかし,このデータは,CSVでありながら,読むのがたいへん難しい形式になっている。これについてはネットでいろいろ論じられている(例:Parsing the Infamous Japanese Postal CSV)。

実際に KEN_ALL.CSV を読んでみよう。文字コードはCP932(SJIS)である。最初の3列は整数であるが頭の0を読み飛ばさないように文字列扱いする:

import pandas as pd

df = pd.read_csv('KEN_ALL.CSV', encoding='cp932', header=None,
                 dtype={0:str, 1:str, 2:str})

説明によれば14番目の列(df[13])は更新状態(0: 変更なし,1: 変更あり,2: 廃止)である。df[13].value_counts() と打ち込んでみると

0    124484
1        18

で廃止データは含まれていないようである。

3番目の列(df[2])が郵便番号である。df[2].value_counts() と打ち込んでみると

4520961    66
4801103    65
4410302    46
7793405    44
0294205    42
           ..
1630409     1
2860033     1
0900037     1
8020092     1
8700243     1

つまり郵便番号 4520961 は66行もあることになる。中を覗いてみると,

23233,"452  ","4520961","アイチケン","キヨスシ","ハルヒアケガワラ","愛知県","清須市","春日明河原",0,0,0,1,0,0
23233,"452  ","4520961","アイチケン","キヨスシ","ハルヒイチバンワリ","愛知県","清須市","春日一番割",0,0,0,1,0,0
23233,"452  ","4520961","アイチケン","キヨスシ","ハルヒイチヤシキ","愛知県","清須市","春日壱屋敷",0,0,0,1,0,0
23233,"452  ","4520961","アイチケン","キヨスシ","ハルヒイッポンマツ","愛知県","清須市","春日一本松",0,0,0,1,0,0
23233,"452  ","4520961","アイチケン","キヨスシ","ハルヒイナリ","愛知県","清須市","春日稲荷",0,0,0,1,0,0
23233,"452  ","4520961","アイチケン","キヨスシ","ハルヒイリマエ","愛知県","清須市","春日杁前",0,0,0,1,0,0

のように延々と続く。df[[6,7]][df[2] == '4520961'].value_counts() としてみると

6    7  
愛知県  清須市    66

となってすべて清須市であり,この場合は 4520961 と入力すれば「愛知県清須市」まで出力すればよいだろう(これら以外にも清須市はある)。

このような「一つの郵便番号で二以上の町域を表す場合」については df[12] が 1 になっていることからもわかる。

では逆に df[12] が 0 になっていれば郵便番号は一意かというと,そうでもなさそうである。df[2][df[12] == 0].value_counts() としてみると

6308037    8
6028368    8
6028374    8
7712102    6
6020816    6
          ..

という具合である。例えば 6308037 は

29201,"630  ","6308037","ナラケン","ナラシ","ナカマチ(5115-5149、5171、5183、5186、","奈良県","奈良市","中町(5115〜5149、5171、5183、5186、",1,0,0,0,0,0
29201,"630  ","6308037","ナラケン","ナラシ","5192-5196、5198-5212、5227-5229、","奈良県","奈良市","5192〜5196、5198〜5212、5227〜5229、",1,0,0,0,0,0
29201,"630  ","6308037","ナラケン","ナラシ","5239-5257、5263、5269-5271、5275、","奈良県","奈良市","5239〜5257、5263、5269〜5271、5275、",1,0,0,0,0,0
29201,"630  ","6308037","ナラケン","ナラシ","5276-4、5276-5、5277-5280、5284-","奈良県","奈良市","5276−4、5276−5、5277〜5280、5284〜",1,0,0,0,0,0
29201,"630  ","6308037","ナラケン","ナラシ","5312、5314、5316-5327、5346、5349、","奈良県","奈良市","5312、5314、5316〜5327、5346、5349、",1,0,0,0,0,0
29201,"630  ","6308037","ナラケン","ナラシ","5351-5352、5355-5362、5364-5424、","奈良県","奈良市","5351〜5352、5355〜5362、5364〜5424、",1,0,0,0,0,0
29201,"630  ","6308037","ナラケン","ナラシ","5426、5429-5430、5445、5455、5461、","奈良県","奈良市","5426、5429〜5430、5445、5455、5461、",1,0,0,0,0,0
29201,"630  ","6308037","ナラケン","ナラシ","5463、5470、5472、5475、5482-5529)","奈良県","奈良市","5463、5470、5472、5475、5482〜5529)",1,0,0,0,0,0

となっており,カッコ書きが長いので複数行に分けているようである。このような場合は本来は 6308037 は「奈良県奈良市中町」までにして,カッコ書きは別フィールドにするべきであろう。とりあえずは最初に見つかった項目のカッコ以下を外して表示すればよさそうである。

町域名については,df[8].value_counts() すると,

以下に掲載がない場合               1874
本町                        310
栄町                        222
新町                        171
幸町                        157
                         ... 

のように「以下に記載がない場合」が非常に多いが,これは町域名として表示しないほうがよさそうである。ほかに「○○町一円」「○○村一円」のようなケースもあるが,これらも「一円」は表示しないのがよさそうである。

以上のような点に注意してデータクレンジングしたものが posuto というPythonパッケージおよび JSON ファイルで用意されている。

[2021-02-10 追記] ケンオールという有料サービスができているようだ。