2
0
はじめての記事投稿
Qiita Engineer Festa20242024年7月17日まで開催中!

市区町村名を県名に変換する辞書を PowerShell でつくる

Posted at

tl;dr

PowerShell と Heartrails Geo API を使って、市の名前をキー、県の名前を値に取る JSON を作ります

# まず 県→市の辞書を作る

$PrefRaw = wget "https://geoapi.heartrails.com/api/json?method=getPrefectures"
$PrefData = $PrefRaw.Content | ConvertFrom-Json
$Prefs = $PrefData.response.prefecture

$out = [System.Collections.Generic.Dictionary[String, PSObject]]::new()
foreach ($Pref in $Prefs) { # 各県について
  $CityRaw = wget "https://geoapi.heartrails.com/api/json?method=getCities&prefecture=$Pref" # 市を取ってきて
  $Data = $CityRaw.Content | ConvertFrom-Json # JSON→Dictionaryにして
  $out.Add($Pref, $Data.response.location) # 追加する
  echo $Data.response.location
  Start-Sleep -m 1000 # 負荷防止
}
# 県→市
echo $out | ConvertTo-Json | Out-File .\out.json

# 市→県の構造に変換
$rawJson = Get-Content .\out.json  # 県→市
$data = $rawJson | ConvertFrom-Json
$cityToPref = [System.Collections.Generic.Dictionary[String, PSObject]]::new() # 辞書を作って
# 件名を取得, Name をキーにしてぜんぶのメンバを取る
$Prefs = $data | Get-Member -MemberType Properties | Select-Object -ExpandProperty Name

foreach ($Pref in $Prefs) {
  $Cities = $data.$Pref.value # ある県から
  foreach ($City in $Cities) { # すべての市に対し
    $cityToPref.Add($City.city, $Pref) # 市→県の辞書に追加
  }
}

$JData = $cityToPref | ConvertTo-Json  # JSON にして
echo $JData | Out-File .\cityToPref.json # 書き出す

上を実行すると得られる cityToPref.json は以下のようになります。

{
    "津市":  "三重県",
    "四日市市":  "三重県",
    "伊勢市":  "三重県",
    "松阪市":  "三重県",
    ...
}

イントロ

あるデータ分析の都合上、"市"の名前からそれが属する"県"の名前を取得する API が欲しかったので、そのベースとなる辞書を作ってみました。

の API から、日本に存在する県名、県に存在する市町村のリストなどいろんなのが取得できます。

もうちょっと詳しいモチベーション

総務省統計局, 家計調査(二人以上の世帯) 品目別都道府県庁所在市及び政令指定都市(※)ランキング
https://www.stat.go.jp/data/kakei/5.htm

を分析して消費額を地図にしようと思ったとき、総務省統計局のデータは「政令指定都市のデータ」が記載され、政令指定都市名(e.g. "仙台市", "堺市")がキーとなっていました。
さて、このデータを Microsoft Excel でWebマップを使って可視化するとなるとWebマップに与えるキーを県名にする必要があったので、市->県に逆引きするデータベースが欲しくなりました。

Excel は JSON をサポートしているので、
{市区町村名: "県名"}
の構造のデータなら、あとは Power Query や VLOOKUP 関数を組み合わせると簡易的に市->県への変換ができます。

解説、いきさつ

県→市 に変換

さて、PowerShellを使って

$PrefRaw = wget "<https://geoapi.heartrails.com/api/json?method=getPrefectures>"

とすると、以下の出力が得られます。

StatusCode        : 200
StatusDescription : OK
Content           : {"response":{"prefecture":["\\u5317\\u6d77\\u9053","\\u9752\\u68ee\\u770c","\\u5ca9\\u624b\\u770c","\\u5bae\\
                    u57ce\\u770c","\\u79cb\\u7530\\u770c","\\u5c71\\u5f62\\u770c","\\u798f\\u5cf6\\u770c","\\u8328\\u57ce\\u770c","
                    \\u68...
RawContent        : HTTP/1.1 200 OK
...以下省略

$PrefRaw はいわゆるオブジェクト

echo $PrefRaw.Content

のように プロパティ Content を読むと

{"response":{"prefecture":["\\u5317\\u6d77\\u9053","\\u9752\\u68ee\\u770c", ...]}}

のような出力が得られます。

JSON の (root)."response"."prefecture" が、県名のリストを含んでいるので、まずJSON(のテキスト)をオブジェクトに変換しましょう。PowerShell だと ConvertFrom-Json コマンドを使えば一発で変換できます。
(今回の場合、変換先がオブジェクトになりました)

$Resp = $PrefRaw.Content | ConvertFrom-Json
$Prefs = $Resp.response.prefecture

ここで

echo $Prefs

とすると

北海道
青森県
岩手県
宮城県
秋田県
山形県
福島県
茨城県
栃木県
...以下略

が得られます(47都道府県あることを $Prefs.lengthで確認するとよいです)

今回使ったAPIは 県->市 の変換ができるので、とりあえず正直に 県->市 に変換できる辞書を作ってみます。

面倒なことは 辞書型 (Dictionary) でなんとかします

$out = [System.Collections.Generic.Dictionary[String, PSObject]]::new()
foreach ($Pref in $Prefs) {
  # API から市データを取得
  $CityRaw = wget "<https://geoapi.heartrails.com/api/json?method=getCities&prefecture=$Pref>"
  $Data = $CityRaw.Content | ConvertFrom-Json
  $out.Add($Pref, $Data.response.location)
  #echo $Data.response.location
  # APIへの過剰な負荷を防止するため
  Start-Sleep -m 1000
}

市データは、たとえば

wget "<https://geoapi.heartrails.com/api/json?method=getCities&prefecture=秋田県>"

とすれば

StatusCode        : 200
StatusDescription : OK
Content           : {"response":{"location":[{"city":"\\u79cb\\u7530\\u5e02","city_kana":"\\u3042\\u304d\\u305f\\u3057"},{"ci
                    ty":"\\u80fd\\u4ee3\\u5e02","city_kana":"\\u306e\\u3057\\u308d\\u3057"},{"city":"\\u6a2a\\u624b\\u5e02","cit
                    y_ka...
RawContent        : HTTP/1.1 200 OK

のように、JSONの (root)."response"."location" に、

{
  "city": "string"
  "city_kana": "string"
}

で表されるオブジェクトが配列になって返ります。
そこで、辞書型配列に

キー: $Pref (県名)
値: $Data.response.location (市、オブジェクト)

のペアを格納しています。

最後に、$out の内容を JSON に書き出しましょう。
ファイル .\\out.json に書き出すなら、

echo $out | ConvertTo-Json | Out-File .\\out.json

でOKです。

{
  "北海道": {
    "value": [
      {
        "city": "札幌市中央区",
        "city_kana": "さっぽろしちゅうおうく"
      },
      {
        "city": "札幌市北区",
        "city_kana": "さっぽろしきたく"
      },
      ...
    ],
    "Count": 188
  },
  "青森県": {
    "value": [
      {
        "city": "青森市",
        "city_kana": "あおもりし"
      },
      {
        "city": "弘前市",
        "city_kana": "ひろさきし"
      },
      ...
    ]
  }
  ...
}

"Count" という不要なプロパティが出てきましたが、加工時に破棄するので、ひとまずはこのままで続行します。

市→県 に変換

続いて、これを 市->県名 の形式に書き換えます。いったん .\\Out.json に書き出したものを再び読み込んで作業します。

$rawJson = Get-Content .\\Out.json
$data = $rawJson | ConvertFrom-Json

$cityToPref = [System.Collections.Generic.Dictionary[String, PSObject]]::new()

県名は $data (辞書型配列) のキーにあたりますが、それを取得するのがちょっと難しいです (C# みたいに $data.Keys で取得できれば便利なのですが)
$data のプロパティを全て読んで、その中の Name と名のつくプロパティだけをすべて抽出します。

$Prefs = $data | Get-Member -MemberType Properties | Select-Object -ExpandProperty Name

こうしたら、後は $cityToPref (辞書型配列) を、市区町村名をキー、県名を値にして作成しま
す。

foreach ($Pref in $Prefs) {
  $Cities = $data.$Pref.value
  foreach ($City in $Cities) {
    $cityToPref.Add($City.city, $Pref)
  }
}

最後に JSON に変換してファイルに書き出します。

$JData = $cityToPref | ConvertTo-Json  # JSON にして
echo $JData | Out-File .\cityToPref.json # 書き出す

あとはこれを Excel の [データ] -> データの取得 -> [ファイルから] -> [JSON] とたどって、 Power Query 経由で落とし込めば目標を達成できそうです。
無事に市区町村名->県名に変換する辞書ができました!

{
    "津市":  "三重県",
    "四日市市":  "三重県",
    "伊勢市":  "三重県",
    "松阪市":  "三重県",
    ...
}

あとがきと注意

ちょっとした問題として、日本には市区町村名がまったく同じ場所が存在します。
この場合、辞書型配列のキーはユニーク(他とかぶらない)である必要があるので、既に存在するキーを追加しようとすると弾かれます。
この場合、市->県の逆引きは2つ以上の検索結果を持ちうることになるため、なにかと判定が難しかったりします…。

今回は政令指定都市->県名 に変換するだけだったので影響は小さいなと判断し無視しましたが、すべての市区町村に対応するにはこの構造は適していません。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0