1
0

はじめに

本記事は国土交通データプラットフォームアドベントカレンダー13日目の記事です。

みなさん こんにちは!

先日、原宿や浅草を訪れたのですが訪日外国人の方々が多くて驚きました。
着実にインバウンドは回復してますね!

日本政府観光局(JNTO)のウェブサイトに掲載されているグラフを見ると今年の9月以降はコロナ禍前の水準に戻りそうです。

DB_1. 訪日外客数の推移_月別1. グラフ1. 日本語 (1).png
日本政府観光局(JNTO) 月別 訪日外客数の推移

そうなると気になるのがオーバーツーリズム。
これから年末年始を迎えるにあたり帰省や旅行で混雑を避けたいと考える方も多いのではないでしょうか。

そこで公開されているデータを活用して訪日外国人の動きを分析したいと思います。

訪日外国人のデータについては前述の日本政府観光局が公開しているものの他に
RESAS 地域経済分析システムでも以下の情報をAPIで取得することができます。

  • 指定地域への国籍別訪問者数
  • 指定国籍からの訪問者数
  • 入出国空港間で訪問した地域の訪問者数
  • 入国空港・出国空港内訳
  • 滞在地域内訳

また国土交通省ではFF-Data(Flow of Foreigners-Data:訪日外国人流動データ)として訪日外国人の都道府県間流動表を作成・公表を行っています。
FF-Dataは国土交通データプラットフォームにも一部連携されており、交通機関別の国内訪問地間の流動量をAPIでデータを取得できることができます。
今回はこのFF-DataとPythonを使って訪日外国人の動きを分析します。

取得できるデータの確認

まずどのような情報が取得できるか確認するため、GraphiQLでdataCatalogAPIを実行します。(QraphiQLって?という方は6日目の記事API紹介①APIって使ったことある? #1をご確認ください!)
カタログIDにffdを指定し次のリクエストを送信します。

query {
  dataCatalog(IDs: "ffd") {
    id
    title
    datasets{
      id
      title
      data_count
      datatype_desc {
        name
        attributes {
          name
          display_name
          description
        }
      }
    }
  }
}

上記のリクエストを送信すると次のような結果が返ってきます。

{
  "data": {
    "dataCatalog": [
      {
        "id": "ffd",
        "title": "FF-Data(訪日外国人流動データ)",
        "datasets": [
          {
            "id": "ffd_2014",
            "title": "2014年",
            "data_count": "2304",
            "datatype_desc": {
              "name": "2014年メタデータ情報",
              "attributes": [
                {
                  "name": "FFD:departure_point",
                  "display_name": "出発地",
                  "description": null
                },
                {
                  "name": "FFD:destination_point",
                  "display_name": "目的地",
                  "description": null
                },
                {
                  "name": "FFD:all",
                  "display_name": "全機関【千人/年】",
                  "description": null
                },
                {
                  "name": "FFD:bus",
                  "display_name": "バス【千人/年】",
                  "description": null
                },
                {
                  "name": "FFD:railway",
                  "display_name": "鉄道【千人/年】",
                  "description": null
                },
                {
                  "name": "FFD:taxi_n_chauffeur",
                  "display_name": "タクシー・ハイヤー【千人/年】",
                  "description": null
                },
                {
                  "name": "FFD:rental_car",
                  "display_name": "レンタカー【千人/年】",
                  "description": null
                },
                {
                  "name": "FFD:other_car",
                  "display_name": "その他の乗用車【千人/年】",
                  "description": null
                },
                {
                  "name": "FFD:domestic_flight",
                  "display_name": "国内線飛行機【千人/年】",
                  "description": null
                },
                {
                  "name": "FFD:other_transportation",
                  "display_name": "その他【千人/年】",
                  "description": null
                },
                {
                  "name": "FFD:transportation_unknown",
                  "display_name": "不明【千人/年】",
                  "description": null
                },
                {
                  "name": "DPF:title",
                  "display_name": "タイトル",
                  "description": null
                },
                {
                  "name": "DPF:prefecture_code",
                  "display_name": "都道府県コード",
                  "description": null
                },
                {
                  "name": "DPF:longitude",
                  "display_name": "経度",
                  "description": null
                },
                {
                  "name": "DPF:latitude",
                  "display_name": "緯度",
                  "description": null
                },
                {
                  "name": "DPF:completion_datetime",
                  "display_name": "完成日時",
                  "description": null
                },
                {
                  "name": "DPF:completion_datetime_originalform",
                  "display_name": "完成日時元フォーマット",
                  "description": null
                },
                {
                  "name": "DPF:year",
                  "display_name": "年度",
                  "description": null
                },
                {
                  "name": "DPF:year_originalform",
                  "display_name": "年度元フォーマット",
                  "description": null
                },
                {
                  "name": "DPF:catalog_id",
                  "display_name": "カタログ",
                  "description": null
                },
                {
                  "name": "DPF:dataset_id",
                  "display_name": "データセット",
                  "description": null
                },
                {
                  "name": "DPF:prefecture_name",
                  "display_name": "都道府県",
                  "description": null
                },
                {
                  "name": "DPF:municipality_name",
                  "display_name": "市区町村",
                  "description": null
                },
                {
                  "name": "DPF:dpf_update_date",
                  "display_name": "連携更新日",
                  "description": null
                }
              ]
            }
          },
	  ///  ///
        ]	  
      }
    ]
  }
}

年度ごとに出発地・目的地間の交通手段別流動量を取得できることが分かりました。
ではPythonを用いてデータの取得・集計を行っていきます。

ライブラリの読み込み

まず必要なライブラリを読み込みます。

import requests
import json
import pandas as pd
from typing import Union

データを取得する関数の作成

次に国土交通データプラットフォームにリクエストを送信する関数を定義します。
リクエストボディに設定するGraphQLを引数で受け取り、結果を辞書型で返します。

def send_request(query: str) -> dict:
    """_summary_

    Args:
        query (str): QraphQL query string

    Returns:
        dict: MLIT DPF API response
    """
    END_POINT: str = "https://www.mlit-data.jp/api/v1/"
    API_KEY: str = "enter your api ke"

    response: requests.models.Response = requests.post(
        END_POINT,
        headers={
            "Content-type": "application/json",
            "apikey": API_KEY,
        },
        json={"query": query},
    )
    result: dict = json.loads(response.content)["data"]
    return result

最後にFF-Dataを取得する関数を定義します。
FF-DataのデータセットIDはffd_2014~ffd_2019のため年度を引数で受け取り、結果をデータフレームで返します。
国土交通データプラットフォームのsearchAPIはDataClassを返すのですがJSONObjectとして定義されているmetadataフィールドについては取得するsubfieldsを指定できないようです。(attributeFilterの条件としては使用できるのに・・・)
そのためmetadataフィールドのうち必要な項目のみ返すようにしています。

def get_ffdata(year: Union[str, int]) -> pd.DataFrame:
    """_summary_

    Args:
        year (str, int): Fiscal Year

    Returns:
        pd.DataFrame: FF-Data DataFrame
    """
    graphql_query: str = """
    query {
      search(
        term:"",
        attributeFilter:{
          attributeName: "DPF:dataset_id"
          is: "ffd_%d"
        }
        first:0,
        size:3000,
    ) {
        totalNumber
        searchResults {
          id
          metadata
          shape
        }
      }
    }
    """ % (
        year
    )

    data: dict = send_request(query=graphql_query)
    df: pd.DataFrame = pd.json_normalize(data["search"]["searchResults"])
    selected_columns: list = [
        "id",
        "shape.coordinates",
        "metadata.DPF:year",
        "metadata.FFD:departure_point",
        "metadata.FFD:destination_point",
        "metadata.FFD:bus",
        "metadata.FFD:railway",
        "metadata.FFD:taxi_n_chauffeur",
        "metadata.FFD:rental_car",
        "metadata.FFD:other_car",
        "metadata.FFD:domestic_flight",
        "metadata.FFD:other_transportation",
        "metadata.FFD:transportation_unknown",
    ]

    return df[selected_columns]

データ取得

データを取得する準備ができたので2017~2019年度のデータを取得します。

ff_data: pd.DataFrame = pd.concat(
    map(get_ffdata, [2017, 2018, 2019]), ignore_index=True
)
ff_data.head()
id shape.coordinates metadata.DPF:year metadata.FFD:departure_point metadata.FFD:destination_point metadata.FFD:bus metadata.FFD:railway metadata.FFD:taxi_n_chauffeur metadata.FFD:rental_car metadata.FFD:other_car metadata.FFD:domestic_flight metadata.FFD:other_transportation metadata.FFD:transportation_unknown
0 0ce25d45-3efe-4419-9b5d-78b37a19e2b6 [[141.34694444444446, 43.06444444444444], [139.8836111111111, 36.56583333333333]] 2016 北海道 栃木 0 1.812 0 0 0 0 0 0
1 b652b574-58ba-4f40-b874-35abf1b343b8 [[141.34694444444446, 43.06444444444444], [133.935, 34.66166666666666]] 2016 北海道 岡山 0 0 0 0 0 0 0 0
2 6a16865c-dd09-4a40-bfa4-26a7f9f2313e [[141.34694444444446, 43.06444444444444], [130.29916666666668, 33.24944444444444]] 2016 北海道 佐賀 0 0 0 0 0 0 0 0
3 d8269d74-0fab-4666-8814-b804466b1875 [[141.34694444444446, 43.06444444444444], [135.86833333333334, 35.004444444444445]] 2016 北海道 滋賀 0 0 0 0 0 0 0 0
4 bccff60a-8538-4978-aab5-165d3e753e44 [[141.34694444444446, 43.06444444444444], [136.62555555555556, 36.594722222222224]] 2016 北海道 石川 0 0.995 0 0 0 0 0 0

無事にデータを取得することができました!
列名が長くて使いにくいのでリネームします。

renamed_data = ff_data.rename(
    columns={
        "shape.coordinates": "geometry",
        "metadata.DPF:year": "year",
        "metadata.FFD:departure_point": "dept",
        "metadata.FFD:destination_point": "dest",
        "metadata.FFD:bus": "bus",
        "metadata.FFD:railway": "railway",
        "metadata.FFD:taxi_n_chauffeur": "taxi",
        "metadata.FFD:rental_car": "rental_car",
        "metadata.FFD:other_car": "other_car",
        "metadata.FFD:domestic_flight": "domestic_flight",
        "metadata.FFD:other_transportation": "other",
        "metadata.FFD:transportation_unknown": "unknown",
    }
)
renamed_data.head()
id geometry year dept dest bus railway taxi rental_car other_car domestic_flight other unknown
0 0ce25d45-3efe-4419-9b5d-78b37a19e2b6 [[141.34694444444446, 43.06444444444444], [139.8836111111111, 36.56583333333333]] 2016 北海道 栃木 0 1.812 0 0 0 0 0 0
1 b652b574-58ba-4f40-b874-35abf1b343b8 [[141.34694444444446, 43.06444444444444], [133.935, 34.66166666666666]] 2016 北海道 岡山 0 0 0 0 0 0 0 0
2 6a16865c-dd09-4a40-bfa4-26a7f9f2313e [[141.34694444444446, 43.06444444444444], [130.29916666666668, 33.24944444444444]] 2016 北海道 佐賀 0 0 0 0 0 0 0 0
3 d8269d74-0fab-4666-8814-b804466b1875 [[141.34694444444446, 43.06444444444444], [135.86833333333334, 35.004444444444445]] 2016 北海道 滋賀 0 0 0 0 0 0 0 0
4 bccff60a-8538-4978-aab5-165d3e753e44 [[141.34694444444446, 43.06444444444444], [136.62555555555556, 36.594722222222224]] 2016 北海道 石川 0 0.995 0 0 0 0 0 0

交通手段が横持ちのため集計しずらいため縦持ちに変換します。

longer_data = pd.melt(
    renamed_data,
    id_vars=["id", "geometry", "year", "dept", "dest"],
    value_vars=[
        "bus",
        "railway",
        "taxi",
        "rental_car",
        "other_car",
        "domestic_flight",
        "other",
        "unknown",
    ],
    var_name="mode",
    value_name="n",
)
id geometry year dept dest mode n
0 0ce25d45-3efe-4419-9b5d-78b37a19e2b6 [[141.34694444444446, 43.06444444444444], [139.8836111111111, 36.56583333333333]] 2016 北海道 栃木 bus 0
1 b652b574-58ba-4f40-b874-35abf1b343b8 [[141.34694444444446, 43.06444444444444], [133.935, 34.66166666666666]] 2016 北海道 岡山 bus 0
2 6a16865c-dd09-4a40-bfa4-26a7f9f2313e [[141.34694444444446, 43.06444444444444], [130.29916666666668, 33.24944444444444]] 2016 北海道 佐賀 bus 0
3 d8269d74-0fab-4666-8814-b804466b1875 [[141.34694444444446, 43.06444444444444], [135.86833333333334, 35.004444444444445]] 2016 北海道 滋賀 bus 0
4 bccff60a-8538-4978-aab5-165d3e753e44 [[141.34694444444446, 43.06444444444444], [136.62555555555556, 36.594722222222224]] 2016 北海道 石川 bus 0

集計しやすくなりました!

京都の来訪者はどれくらい増えてるの?

私は実家が京都のため、京都を訪れる訪日外国人がどれくらい増えているかが気になります。。。
そのため目的地が京都のデータに絞り込みます。

filtered_data = longer_data[longer_data["dest"] == "京都"]
filtered_data.head()
id geometry year dept dest mode n
14 40a5a037-cc24-4156-9231-0ebd472c4bff [[141.34694444444446, 43.06444444444444], [135.75555555555556, 35.02111111111111]] 2016 北海道 京都 bus 0
81 14dae62e-3649-4b0f-94c8-7f6cccbc1666 [[140.73999999999998, 40.824444444444445], [135.75555555555556, 35.02111111111111]] 2016 青森 京都 bus 0
128 72f2571a-9ea1-497b-82dd-85fde15cfe08 [[141.15277777777777, 39.703611111111115], [135.75555555555556, 35.02111111111111]] 2016 岩手 京都 bus 0
162 f0c7dd66-457b-4ca0-ad1c-2a3e4790b527 [[140.87222222222223, 38.26888888888889], [135.75555555555556, 35.02111111111111]] 2016 宮城 京都 bus 0
208 a630ae32-d3c8-4ad5-ace1-9c7635270acd [[140.1025, 39.718611111111116], [135.75555555555556, 35.02111111111111]] 2016 秋田 京都 bus 0

年度毎の来訪者数を集計します。

grouped_data = filtered_data.groupby("year").agg(flow=("n", "sum")).reset_index()
grouped_data
year flow
0 2016 5399.22
1 2017 5893.33
2 2018 6760.05

グラフにすると次のようになります。

fig = px.bar(grouped_data, x="year", y="flow")
fig.show()

newplot (1).png

単位は千人なので年間で100万人近く増えてきているようです。コロナ禍前の2018年の水準に戻り、円安を考慮すると今年はもっと増えているかもしれないですね。。。

どこから京都に来ているの?

続いて京都に来訪した外国人が京都に訪れる前にどこにいたか確認します。

grouped_data = filtered_data.groupby(['year', 'dept']).agg(flow=('n', 'sum')).reset_index()
fig = px.treemap(grouped_data, path=['year', 'dept'], values='flow')
fig.show()

newplot (2) (1).png

一番多いのは大阪、次いで東京、奈良となっていますね。その他、新幹線沿いの都道府県が多いようです。

何で京都に来てるの?

最後に京都に来訪した外国人がどの交通手段で訪れているか確認します。

grouped_data = filtered_data.groupby(["year", "mode"]).agg(flow=("n", "sum")).reset_index()
fig = px.treemap(grouped_data, path=["year", "mode"], values="flow")
fig.show()

newplot (3) (1).png

鉄道が最も多く、次いでバスといった順位は変わらないようです。鉄道は新幹線、バスは観光バスで移動される方が多いのでしょうか。

まとめ

以上、国土交通DPFのAPIを使って訪日外国人の動きを確認しました。
FF-Data(訪日外国人流動データ)の概要と利用例のような分析やKepler.glを使った可視化にも取り組みたかったですがPython力がなさすぎた・・・精進します。
ff_data.to_csv("data.csv")でCSVに出力して細かい集計はExcelでやればよかったかな・・・)

今回確認したデータは年間の統計値のため冒頭に記載した目的である年末年始の混雑回避には全く役に立たないのですが、「こんなことができるよ!」といった参考になれば幸いです。

それでは明日もお楽しみに!

1
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
1
0