Skip to content

Instantly share code, notes, and snippets.

@kohiro37
Last active February 24, 2020 08:40
Show Gist options
  • Save kohiro37/5e43ecd1ddd4c6c1ac4df4854a7f5667 to your computer and use it in GitHub Desktop.
Save kohiro37/5e43ecd1ddd4c6c1ac4df4854a7f5667 to your computer and use it in GitHub Desktop.
ipywidgetsとJupyter Notebookで経済センサスのコロプレス図を作成する
"""
【必要なライブラリなど】
- Jupyter Notebook
- Pandas
- GeoPandans
- Plotly
- ipywidgets
【用意するデータ】
- 都道府県境界図
以下サイトのようにe-Statからダウンロードしたデータから作成
PythonとPlotlyでインタラクティブなコロプレス図を作成する
https://irukanobox.blogspot.com/2020/02/pythonplotly.html
あるいは、動作確認はしていないがGeoJSON形式で以下のカラムがあるデータなら動作するはず
・都道府県名(カラム名はPREF_NAME)
・都道府県名と対応するI(カラム名はID)
・都道府県のジオメトリ(カラム名はgeometry)
・都道府県ごとの人口(カラム名はJINKO)
- e-Statからダウンロードした平成28年経済センサス(活動調査)のcsv
統計名:経済センサス‐活動調査 平成28年経済センサス-活動調査 事業所に関する集計 産業別集計 サービス関連産業Bに関する集計
表番号:7
表題:サービス関連産業B(細分類),単独・本所・支所(3区分)別民営事業所数,従業者数,売上(収入)金額及び収入を得た相手先別収入額―全国,都道府県
【補足情報】
詳細は以下ブログを参照
ipywidgetsとJupyter Notebookで経済センサスのコロプレス図を作成する
https://irukanobox.blogspot.com/2020/02/ipywidgetsjupyter-notebook.html
"""
import json
import pandas as pd
import geopandas as gpd
import plotly.express as px
import ipywidgets as widgets
# 都道府県境界図のパス
OUTLINEMAP_PATH = 'jpnmap_simplified.geojson'
# コロプレス図の中心
CENTER = {'lat': 36, 'lon': 140}
def load_sensus7(path_to_csv):
print('*** Loading {} ***'.format(path_to_csv))
# 都道府県をインデックスとして読み込む
# 最終header行より前の行はスキップされる
df = pd.read_csv(path_to_csv, dtype=object, header=[10, 12], index_col=1, skipinitialspace=True, encoding='shift-jis')
# 地域コード、時間軸コード、時間軸、表章項目カラムを削除
df.drop(columns=df.columns[:4], inplace=True)
df.drop(columns=df.columns[1], inplace=True)
# 産業分類をインデックスに追加してマルチインデックスにする
df.set_index(df.columns[0], append=True, inplace=True)
df.index.set_names(['都道府県', '産業分類'], inplace=True)
# set_indexを使うとマルチカラムインデックスがシングルインデックスカラムになってしまうので再設定
df.columns = pd.MultiIndex.from_tuples(df.columns, names=['単独・本所・支所', '項目'])
# 「単独・本所・支所」カラムをマルチインデックスに追加
# stackを実行すると順番が変わるのでreindexで元の順番を維持させる
column_l = [lv1 for lv0, lv1 in df.columns]
column_sorted = sorted(set(column_l), key=column_l.index)
index_sorted = []
base_l = [base for base, item in df.columns]
base_sorted = sorted(set(base_l), key=base_l.index)
for pref, industry in df.index:
for base in base_sorted:
index_sorted.append((pref, industry, base))
df = df.stack(level=0).reindex(index_sorted).reindex(columns=column_sorted)
# 取り込んだデータのカンマを除去して数値に変換
df = df.applymap(lambda x: pd.to_numeric(x.replace(',', ''), errors='coerce'))
# NaNを0とする
# to_numericだとfloatになってしまうのでここでintにする
df = df.fillna(0).astype('int64')
# 「全国」の行を削除
return df.iloc[~(df.index.get_level_values(0) == '全国')]
def load_outlinemap():
outline = gpd.read_file(OUTLINEMAP_PATH, driver='GeoJSON')
# GeoJSON形式で保存するとGeoDataFrameのindexが保存されないので再設定する
# to_jsonのときにindexの値がIDとなるため
outline.set_index('ID', drop=False, inplace=True)
print(outline.info())
display(outline.head())
return outline
def main(path_to_csv_l):
def plot_choropleth(background, colname, **indices):
# 「産業」「単独・本所・支所」で対象データをしぼる
prefs_df = df.copy()
for idx_name, idx_value in indices.items():
prefs_df = prefs_df.iloc[prefs_df.index.get_level_values(idx_name)==idx_value]
# マルチインデックスの都道府県以外のindexを削除する(outlineとの計算でエラーになるため)
prefs_df.index = prefs_df.index.droplevel(prefs_df.index.names[1:])
# outlineとprefs_dfのindexをIDに合わせる
prefs_df.set_index('ID', drop=False, inplace=True)
# 人口1000人当たりの数値にする
prefs_df['color'] = prefs_df[colname] * 1000 / outline['JINKO']
# color: 表示対象の項目
# locations: geojsonのIDに対応する項目
# geojson: geojsonをdictに変換したもの。IDがふられている
fig = px.choropleth_mapbox(prefs_df, geojson=json.loads(outline['geometry'].to_json()), locations='ID', color='color',
title = '',
hover_name = 'PREF_NAME',
color_continuous_scale='OrRd',
mapbox_style=background,
zoom=3.5,
center = CENTER,
opacity=0.5,
labels={colname:colname}
)
fig.update_layout(margin={'r':10,'t':30,'l':10,'b':10, 'pad':5})
fig.show()
# 都道府県境界図の読み込み
outline = load_outlinemap()
df = pd.DataFrame()
for path_to_csv in path_to_csv_l:
df = df.append(load_sensus7(path_to_csv))
# 経済センサスのデータと都道府県境界図のデータを、都道府県名をキーにしてマージ
# dfにgeometryに対応するIDを設定
idx = pd.MultiIndex.from_tuples(df.index.values, names=df.index.names)
df = df.merge(outline[['PREF_NAME', 'ID']], left_on=df.index.names[0], right_on='PREF_NAME', how='left')
df.index = idx
print(df.info())
display(df.head())
# 背景地図のウィジェット
background = widgets.Dropdown(
options=['white-bg', 'carto-positron', 'carto-darkmatter', 'stamen-terrain'],
value='white-bg',
description='背景地図:',
)
# 項目用ウィジット
# PREF_NAMEとIDを対象外にする
item = widgets.Dropdown(
options=df.columns[:-2],
value=df.columns[0],
description='項目:',
)
# index項目用Dropdown作成
dropdown_indices = {}
level_n = df.index.nlevels
for level in range(1, level_n):
index_l = list(df.index.get_level_values(level).values)
index_l_u = sorted(set(index_l), key=index_l.index)
dropdown = widgets.Dropdown(
options=index_l_u,
value=index_l_u[0],
description=df.index.names[level] + ':',
)
dropdown_indices[df.index.names[level]] = dropdown
widgets.interact(plot_choropleth, background=background, colname=item, **dropdown_indices)
if __name__ == '__main__':
# ダウンロードしたcsvを指定
main(['FEH_00200553_200243012738.csv', 'FEH_00200553_200243012807.csv'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment