Last active
February 24, 2020 08:40
-
-
Save kohiro37/5e43ecd1ddd4c6c1ac4df4854a7f5667 to your computer and use it in GitHub Desktop.
ipywidgetsとJupyter Notebookで経済センサスのコロプレス図を作成する
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
【必要なライブラリなど】 | |
- 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