Dashを用いた人流データビジュアライゼーション Part.1
1. やりたいこと
G空間情報センターにて公開されている全国人流オープンデータを用いて、1kmメッシュ別の滞在人口を可視化させること。
具体的には、メッシュにカーソルを合わせてインタラクティブに人口を表示させたり、プルダウンで関心エリアの変更を可能にする機能を実装します。
出典:「全国の人流オープンデータ」(国土交通省)(https://www.geospatial.jp/ ckan/dataset/mlit-1km-fromto)
2. 使用データ概要
人流データの概要・フォーマットや滞在人口の算出方法の詳細は定義書に記載されている通りですが、
となっています。
3. データ前処理
本記事で使用する以下の3ファイルで、取得する都道府県(都道府県コード)は埼玉県(11)、千葉県(12)、東京都(13)、神奈川県(14)とします。
4. jupyter上で滞在人口を可視化する
ディレクトリ構造はこんな感じ
Agoop/
├── data
│ └── {pref_code}_mesh1km
│ │ └── 2019/{mm}/monthly_mdp_mesh1km.csv.zip
│ └── attribute
│ │ └── attribute_mesh1km_2019.csv.zip
│ └── prefcode_citycode_master
│ └── prefcode_citycode_master_utf8_2019.csv.zip
└── src
└── app.ipynb
└── app.py
まずはjupyterでサクッと可視化してみましょう。
2019年1月のdayflag=2(全日)、timezone=2(終日)のデータだけをひとまず可視化することにします。また、各メッシュIDの座標や市区町村名も紐づけておきます。
# app.ipynb
import numpy as np
import pandas as pd
from dfply import *
import geopandas as gpd
import plotly.express as px
import plotly.graph_objects as go
from shapely.geometry import Polygon
attribute_mesh1km = pd.read_csv('../data/attribute/attribute_mesh1km_2019.csv.zip')
prefcode_citycode_master = pd.read_csv('../data/prefcode_citycode_master/prefcode_citycode_master_utf8_2019.csv.zip')
dfs = []
for pref_code in [11, 12, 13, 14]:
df = pd.read_csv(f'../data/{pref_code}_mesh1km/2019/01/monthly_mdp_mesh1km.csv.zip') >> mask(X.dayflag==2, X.timezone==2)
dfs.append(df)
df = pd.concat(dfs).reset_index(drop=True)
df = df >> inner_join(attribute_mesh1km, by=['mesh1kmid', 'prefcode', 'citycode']) >> inner_join(prefcode_citycode_master[['citycode', 'cityname']], by='citycode')
df
あとはplotly.express.choropleth_mapboxメソッドで簡単に可視化できます。※ただし、メッシュIDの四隅の座標からポリゴン作成→GeoJSON形式へ変換する必要がある。
df['geometry'] = df.apply(lambda x: Polygon([(x['lon_min'], x['lat_min']), (x['lon_min'], x['lat_max']), (x['lon_max'], x['lat_max']), (x['lon_max'], x['lat_min'])]), axis=1)
df = df.set_index('mesh1kmid')
fig = px.choropleth_mapbox(df, geojson=gpd.GeoSeries(df['geometry']).__geo_interface__, locations=df.index, color='population',
color_continuous_scale='Jet', center={"lat": df['lat_center'].mean(), "lon": df['lon_center'].mean()},
hover_data=['cityname'], mapbox_style="open-street-map",opacity=0.5,zoom=9, height=600,)
fig.show()
ちなみにdfplyはSQLっぽくテーブル操作できるライブラリです。
5. Dashを用いたデータビジュアライゼーション
では、Dashにうつります。先に完成コードがこちら。
# app.py
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import ast
import json
import pandas as pd
from dfply import *
import geopandas as gpd
import plotly.express as px
from shapely.geometry import Polygon
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
attribute_mesh1km = pd.read_csv('../data/attribute/attribute_mesh1km_2019.csv.zip')
prefcode_citycode_master = pd.read_csv('../data/prefcode_citycode_master/prefcode_citycode_master_utf8_2019.csv.zip')
dfs = []
for pref_code in [11, 12, 13, 14]:
df = pd.read_csv(f'../data/{pref_code}_mesh1km/2019/01/monthly_mdp_mesh1km.csv.zip') >> mask(X.dayflag==2, X.timezone==2)
dfs.append(df)
df = pd.concat(dfs).reset_index(drop=True)
df = df >> inner_join(attribute_mesh1km, by=['mesh1kmid', 'prefcode', 'citycode']) >> inner_join(prefcode_citycode_master[['citycode', 'cityname']], by='citycode')
df['geometry'] = df.apply(lambda x: Polygon([(x['lon_min'], x['lat_min']), (x['lon_min'], x['lat_max']), (x['lon_max'], x['lat_max']), (x['lon_max'], x['lat_min'])]), axis=1)
df = df.set_index('mesh1kmid')
app.layout = html.Div(
[
html.Div(
[
html.H4('都道府県の選択'),
dcc.Dropdown(
id='pref-dropdown',
options=[
{'label': '埼玉県', 'value': 11},
{'label': '千葉県', 'value': 12},
{'label': '東京都', 'value': 13},
{'label': '神奈川県', 'value': 14}
],
value=13,
),
]
),
html.Div(
[
html.H3(id='hoverdata-h3'),
html.Div(dcc.Loading(id='loading', type='circle', children=dcc.Graph(id='population-map')), style={'width': '80%', 'margin': 'auto'}),
],
),
],
style={'width': '100%', 'margin': 'auto', 'textAlign': 'center'},
)
@app.callback(Output('population-map', 'figure'), Input('pref-dropdown', 'value'))
def update_map(value):
df_target = df >> mask(X.prefcode==value)
fig = px.choropleth_mapbox(
df_target, geojson=gpd.GeoSeries(df_target['geometry']).__geo_interface__, locations=df_target.index, color='population',
color_continuous_scale='Jet', center={'lat': df_target['lat_center'].mean(), 'lon': df_target['lon_center'].mean()},
hover_data=['cityname'], mapbox_style='open-street-map',opacity=0.5,zoom=9, height=800,
)
return fig
@app.callback(Output('hoverdata-h3', 'children'), Input('population-map', 'hoverData'))
def update_title(hoverData):
try:
title = ast.literal_eval(json.dumps(hoverData, ensure_ascii=False))
meshcode = title['points'][0]['location']
location = title['points'][0]['customdata'][0]
return f'{location}(地域メッシュコード:{meshcode})'
except:
return 'NULL'
if __name__ == '__main__':
app.run_server(debug=True)
5-1. レイアウト
各コンポーネントにIDをつける
app.layout = html.Div(
[
html.Div(
[
html.H4('都道府県の選択'),
dcc.Dropdown(
id='pref-dropdown',
options=[
{'label': '埼玉県', 'value': 11},
{'label': '千葉県', 'value': 12},
{'label': '東京都', 'value': 13},
{'label': '神奈川県', 'value': 14}
],
value=13, # 初期値の設定
),
]
),
html.Div(
[
html.H3(id='hoverdata-h3'),
html.Div(dcc.Loading(id='loading', type='circle', children=dcc.Graph(id='population-map')), style={'width': '80%', 'margin': 'auto'}),
],
),
],
style={'width': '100%', 'margin': 'auto', 'textAlign': 'center'},
)
5-2. 一つ目のコールバック関数
pref-dropdownのvalue(11〜14)を受け取って抽出した1つをマッピングし、dcc.Graph(id='population-map')に渡します。
@app.callback(Output('population-map', 'figure'), Input('pref-dropdown', 'value'))
def update_map(value):
df_target = df >> mask(X.prefcode==value)
fig = px.choropleth_mapbox(
df_target, geojson=gpd.GeoSeries(df_target['geometry']).__geo_interface__, locations=df_target.index, color='population',
color_continuous_scale='Jet', center={'lat': df_target['lat_center'].mean(), 'lon': df_target['lon_center'].mean()},
hover_data=['cityname'], mapbox_style='open-street-map',opacity=0.5,zoom=9, height=800,
)
return fig
5-3. 二つ目のコールバック関数
地図上のメッシュにカーソルを合わせたときに、該当メッシュの地名と地域メッシュコードをhtml.H3(id='hoverdata-h3')に渡します。
@app.callback(Output('hoverdata-h3', 'children'), Input('population-map', 'hoverData'))
def update_title(hoverData):
try:
title = ast.literal_eval(json.dumps(hoverData, ensure_ascii=False))
meshcode = title['points'][0]['location']
location = title['points'][0]['customdata'][0]
return f'{location}(地域メッシュコード:{meshcode})'
except:
return 'NULL'
参考書籍
Python インタラクティブ・データビジュアライゼーション入門 ―Plotly/Dashによるデータ可視化とWebアプリ構築
この記事が気に入ったらサポートをしてみませんか?