20
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Elixir Livebook で地域経済分析システム RESAS から人口構成を取得して表、グラフで可視化する

Last updated at Posted at 2022-11-16

はじめに

RESAS は日本の地域経済データを取得できるサービスです

地域経済分析システム(RESAS:リーサス)は、地方創生の様々な取り組みを情報面から支援するために、経済産業省と内閣官房デジタル田園都市国家構想実現会議事務局が提供しています。

RESAS-API に利用登録すると、 REST API でデータを取得することもできます

今回は Elixir Livebook 上で Explorer を使って、 RESAS のデータを分析、可視化してみたいと思います

今回実装したノートブックの全量はこちら

出典

この記事はRESAS(地域経済分析システム)のデータを加工して作成しています

実行環境

このリポジトリーの Docker コンテナ上で実行しました

RESAS-API の使い方

公式サイトから「利用登録」に進んで、必要項目を入力して仮登録します

本登録用のメールに記載されているリンクにアクセスすれば登録完了です

スクリーンショット 2022-11-15 22.30.33.png

ここに表示されるAPIキーを利用します

利用登録後は、 RESAS-API マイページの最下部にもAPIキーが表示されます

スクリーンショット 2022-11-15 22.29.36.png

セットアップ

Livebook を起動してノートブックを開きます

以下のコードをノートブック上で実行してください

Mix.install([
  {:httpoison, "~> 1.8"},
  {:json, "~> 1.4"},
  {:explorer, "~> 0.3"},
  {:kino, "~> 0.7"},
  {:kino_vega_lite, "~> 0.1.4"}
])

必要なライブラリをインストールします

  • httpoison: REST API を呼び出す
  • json: JSON をエンコード、デコードする
  • explorer: データ分析
  • kino: 実行結果を可視化する
  • kino_vega_lite: データをグラフ化する

以下のコードを実行するとテキストエリアが表示されるので、 Tellus で作っておいたトークンを入力します

# RESAS のAPIキーを入力する
api_key_input = Kino.Input.password("API_KEY")

ベースAPIを設定します

base_url = "https://opendata.resas-portal.go.jp"

エイリアスを指定しておきます

alias Explorer.DataFrame
alias Explorer.Series

RESAS の認証

RESAS の認証は API キーをヘッダーに入れるだけです

認証ヘッダーを設定しておきます

auth_header = {"X-API-KEY", Kino.Input.read(api_key_input)}

都道府県一覧の取得

公式リファレンスを参照すれば、どういう API をどう呼び出せばどういう内容が返ってくるのかが分かります

都道府県一覧は以下のようにして呼び出します

prefectures_url = "#{base_url}/api/v1/prefectures"

prefectures =
  prefectures_url
  |> HTTPoison.get!([auth_header])
  |> then(&JSON.decode!(&1.body))

スクリーンショット 2022-11-15 23.05.09.png

レスポンスの result に都道府県マップのリストが入っていますね

DataFrame.new() にマップのリストを渡すだけでデータフレームが出来上がります

prefectures_df = DataFrame.new(prefectures["result"])

スクリーンショット 2022-11-15 23.14.21.png

データフレームのままだと見にくいので、表形式にしてみます

Kino.DataTable.new() にデータフレームをそのまま渡すだけです

Kino.DataTable.new(prefectures_df, sorting_enabled: true)

ページングやソートができる表として表示されました

table.gif

続いて大分県の都道府県コードをデータフレームから取得してみましょう

oita_pref_code =
  prefectures_df
  # "prefName" が "大分県" の行を抽出
  |> DataFrame.filter_with(&Series.equal(&1["prefName"], "大分県"))
  # "prefCode" の列を選択
  |> DataFrame.pull("prefCode")
  # 先頭のデータを取得
  |> Series.first()
  |> dbg()

スクリーンショット 2022-11-15 23.28.24.png

最後に dbg を入れたことにより、各パイプラインの途中経過を見ることができます

市区町村一覧の取得

都道府県コードを指定して市区町村一覧を取得します

空文字 "" が渡された場合は全都道府県の市区町村になります

get_cities = fn pref_code ->
  query =
    case pref_code do
      "" ->
        ""
      pref_code ->
        "?prefCode=#{pref_code}"
    end

  cities_url = "#{base_url}/api/v1/cities#{query}"

  cities_url
  |> HTTPoison.get!([auth_header])
  |> then(&JSON.decode!(&1.body))
  |> then(&(&1["result"]))
end

まず全国の市区町村を取得してみましょう

all_cities_df =
  ""
  |> get_cities.()
  |> DataFrame.new()

all_cities_df
|> Kino.DataTable.new(sorting_enabled: true)

スクリーンショット 2022-11-15 23.34.00.png

全国で 1922 の市区町村が取得できました

bigCityFlag は「特別区・行政区フラグ」で、以下の値を持ちます

  • 0:一般の市区町村
  • 1:政令指定都市の区
  • 2:政令指定都市の市
  • 3:東京都23区

大分県内の市町村を取得してみましょう

oita_cities_df =
  oita_pref_code
  |> get_cities.()
  |> DataFrame.new()

oita_cities_df
|> Kino.DataTable.new(sorting_enabled: true)

スクリーンショット 2022-11-15 23.36.55.png

18 市町村が取得できました

後で使うために大分市の市区町村コードを取得しておきます

oita_city_code =
  oita_cities_df
  |> DataFrame.filter_with(&Series.equal(&1["cityName"], "大分市"))
  |> DataFrame.pull("cityCode")
  |> Series.first()
  |> dbg()

スクリーンショット 2022-11-15 23.42.02.png

特別区・行政区のある都道府県

大分県に特別区・行政区があるか見てみましょう

oita_cities_df
|> DataFrame.filter_with(&Series.not_equal(&1["bigCityFlag"], "0"))
|> Kino.DataTable.new(sorting_enabled: true)

スクリーンショット 2022-11-15 23.43.15.png

大分県に特別区・行政区はないですね

全国に特別区・行政区がある都道府県はどれくらいあるのでしょう

all_cities_df
# bigCityFlag が 0 でない行を抽出する
|> DataFrame.filter_with(&Series.not_equal(&1["bigCityFlag"], "0"))
# 都道府県コードで重複排除する
|> DataFrame.distinct(["prefCode"])
# 都道府県一覧データフレームと結合する
|> DataFrame.join(prefectures_df)
|> Kino.DataTable.new(sorting_enabled: true)
|> dbg()

スクリーンショット 2022-11-15 23.46.23.png

はい、上画像の 16 都道府県は特別区・行政区があります

人口構成の取得

いよいよ人口構成(年代別の人口)を取得します

人口構成 API は都道府県コードが必須、市区町コードは指定しない場合 - を渡します

get_population_composition = fn pref_code, city_code ->
  query =
    "?prefCode=#{pref_code}&cityCode="
    <>
    case city_code do
      "" ->
        "-"
      city_code ->
        city_code
    end

  url = "#{base_url}/api/v1/population/composition/perYear#{query}"

  url
  |> HTTPoison.get!([auth_header])
  |> then(&JSON.decode!(&1.body))
  |> then(&(&1["result"]["data"]))
end

大分県全体の人口構成を取得します

population_composition = get_population_composition.(oita_pref_code, "")

スクリーンショット 2022-11-15 23.54.01.png

レスポンスは以下のような、ちょっと特殊な形になっています

  %{
    "data" => [
      %{"value" => 1239655, "year" => 1960},
      %{"value" => 1187480, "year" => 1965},
      ...
      %{"value" => 946917, "year" => 2040},
      %{"value" => 896653, "year" => 2045}
    ],
    "label" => "総人口"
  },
  %{
    "data" => [
      %{"rate" => 32.6, "value" => 405103, "year" => 1960},
      %{"rate" => 27.7, "value" => 329717, "year" => 1965},
      ...
      %{"rate" => 11, "value" => 105073, "year" => 2040},
      %{"rate" => 11, "value" => 99257, "year" => 2045}
    ],
    "label" => "年少人口"
  },
  %{
    "data" => [
      %{"rate" => 60.1, "value" => 746271, "year" => 1960},
      %{"rate" => 63.9, "value" => 759450, "year" => 1965},
      ...
      %{"rate" => 50.8, "value" => 481160, "year" => 2040},
      %{"rate" => 49.5, "value" => 444584, "year" => 2045}
    ],
    "label" => "生産年齢人口"
  },
  %{
    "data" => [
      %{"rate" => 7.1, "value" => 88281, "year" => 1960},
      %{"rate" => 8.2, "value" => 98313, "year" => 1965},
      ...
      %{"rate" => 38, "value" => 360684, "year" => 2040},
      %{"rate" => 39.3, "value" => 352812, "year" => 2045}
    ],
    "label" => "老年人口"
  }
]

このままだとデータフレームにできないので、ちょっと加工してからデータフレームにします

population_composition_df =
  population_composition
  |> Enum.flat_map(fn data ->
    data["data"]
    |> Enum.map(fn datum ->
      Map.merge(datum, %{"label" => data["label"]})
    end)
  end)
  |> DataFrame.new()

population_composition_df
|> Kino.DataTable.new(sorting_enabled: true)

スクリーンショット 2022-11-16 0.00.29.png

ではグラフにしてみましょう

楽をしたいので Smart Cells を使います

セル追加するときに +Smart から Chart を選びます

スクリーンショット 2022-11-16 0.01.19.png

すると以下のようなセルが追加されます

スクリーンショット 2022-11-16 0.04.16.png

以下のように入力してセルを実行しましょう

  • CHARTING: 大分県人口構成推移
  • WIDTH: 700
  • HEIGHT: 400
  • Data: population_composition_df
  • Chart: line
  • x-axis: year
  • y-axis: value
  • Color: label

すると、以下のようにグラフが表示できます

スクリーンショット 2022-11-16 0.07.54.png

ちなみに2015年までは実績で、それより後は予測値です

人口減少と高齢化が見えて悲しいですね

大分市で見てみましょう

oita_city_population_composition_df =
  oita_pref_code
  |> get_population_composition.(oita_city_code)
  |> Enum.flat_map(fn data ->
    data["data"]
    |> Enum.map(fn datum ->
      Map.merge(datum, %{"label" => data["label"]})
    end)
  end)
  |> DataFrame.new()

oita_city_population_composition_df
|> Kino.DataTable.new(sorting_enabled: true)

スクリーンショット 2022-11-16 0.10.09.png

大分県全体と比べるとマシですが、やはり人口減少と高齢化が進むように予測されています

まとめ

RESAS-API と Explore を使えばもっと色々な分析、可視化ができそうですね

20
8
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
20
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?