これはMYJLab Advent Calendar 2019の21日目の記事です。サッチーが担当いたします。(遅れてすみませんでした)
今回は
すること
最近道路を挟んで向かい側同士にファミリマートがある、みたいな現象が私の地元では起きています。この現象を可視化すべく、今回はファミリーマートの店舗数の増加を都道府県ごとに時系列ヒートマップに落としてみようと思います。
使うもの
LeafletというJavaScriptでいい感じのマップを描画できるライブラリがあります。今回はPythonでLeafletを使うことができるようにしてくれるfoliumというライブラリを使って時系列ヒートマップを作成します。
早速描く
実行環境
- Jupyter Notebook
- Python 3.7.3
- folium 0.10.1
foliumはバージョンによって機能や使い方がかわってくるので、今回は必ずバージョンを0.10.1にするようにしてください。
# ライブラリ読み込み
import pandas as pd
import folium
from folium import plugins
データ準備
こちらのサイトにあった1999年から2019年のファミリーマートの都道府県ごとの店舗数をcsvファイルにし直してデータを用意しました。PythonのRequestsで落とせるようにしてあります。また、地図に描画するにあたって各県の緯度経度が必要です。今回は都道府県庁が存在する場所を各県の緯度経度とします。データはこちらからダウンロードできます。
# 店舗数のデータ
import requests
import io
URL = "https://drive.google.com/uc?id=1-8tppvHwwVJWufYVskTfGz7cCrBIE0SM"
r = requests.get(URL)
famima_data = pd.read_csv(io.BytesIO(r.content))
famima_data.head()
近年増えているというだけあって、1999~2006年はかなり欠損値が多くなっています。
# 都道府県庁の緯度経度
geo_data = pd.read_csv("./data/prefecturalCapital.csv")
geo_data.head()
次に、この2つのデータフレームを結合します。idをキーにして結合したいので、geo_dataのidを0始まりに直して結合します。欠損値は一旦0とします。
import numpy as np
geo_data.id = geo_data.id - 1
merged_data = pd.merge(famima_data, geo_data[["id", "lat", "lon"]], on=["id"])
merged_data = merged_data.replace(np.nan, 0)
merged_data.head()
基本のデータの準備ができました。
増加のデータに変換していく
今回は店舗数の増減の推移を可視化したいので列で差分をとります。
# 時系列のカラム名の配列を取得する
time_columns = merged_data.columns[2:23].values
# 店舗数のデータの部分のみ差分をとり、diff_dataとする
merged_data.loc[:, time_columns] = merged_data.loc[:, time_columns].astype(float)
diff_data = merged_data.copy()
diff_data.loc[:, time_columns] = merged_data.loc[:, time_columns].diff(axis=1)
# 1999年のデータがなくなるので削除する
diff_data = diff_data.dropna(axis=1)
time_columns = time_columns[1:]
diff_data.head()
差分が取れたらmin-max-scalingをします。foliumのヒートマップでは0があるとなにかと不都合なので、全体に1e-4を足します。
# diff_dataをスケーリングし、scaled_dataとする
scaled_data = diff_data.copy()
scaled_data.loc[:, time_columns] = (diff_data.loc[:, time_columns] - diff_data.loc[:, time_columns] .min().min()) / (diff_data.loc[:, time_columns] .max().max() - diff_data.loc[:, time_columns] .min().min())
scaled_data.loc[:, time_columns] = scaled_data.loc[:, time_columns] + 1e-4
scaled_data.head()
最後に、時系列ヒートマップを描画するために
[[[緯度, 経度, データ] * 47都道府県] * 1999~2019]となる3次元データを作成します。
heat_map_data = [[[row['lat'],row['lon'], row[idx]] for index, row in scaled_data.iterrows()] for idx in time_columns]
# データの形がわかりにくいので一つ目だけ出力
heat_map_data[0]
#出力
[[43.064359, 141.347449, 0.051760516605166056],
[40.824294, 140.74005400000001, 0.051760516605166056],
[39.70353, 141.15266699999998, 0.05545055350553506],
[38.268737, 140.872183, 0.060985608856088565],
[39.718175, 140.10335600000002, 0.051760516605166056],
[38.240127, 140.362533, 0.07390073800738008],
[37.750146, 140.466754, 0.0923509225092251],
[36.341817, 140.446796, 0.04807047970479705],
[36.56575, 139.883526, 0.05545055350553506],
[36.391205, 139.060917, 0.060985608856088565],
[35.857771, 139.647804, 0.060985608856088565],
[35.604563, 140.123179, 0.04807047970479705],
[35.689184999999995, 139.691648, 0.0997309963099631],
[35.447505, 139.642347, 0.06467564575645757],
[37.901699, 139.022728, 0.051760516605166056],
[36.695274, 137.211302, 0.06467564575645757],
[36.594729, 136.62555, 0.06467564575645757],
[36.065220000000004, 136.221641, 0.06283062730627306],
[35.665102000000005, 138.568985, 0.05545055350553506],
[36.651282, 138.180972, 0.051760516605166056],
[35.39116, 136.722204, 0.05729557195571956],
[34.976987, 138.383057, 0.05729557195571956],
[35.180246999999994, 136.906698, 0.07574575645756458],
[34.730546999999994, 136.50861, 0.06836568265682658],
[35.004532, 135.868588, 0.05360553505535055],
[35.020996200000006, 135.7531135, 0.05360553505535055],
[34.686492, 135.518992, 0.0978859778597786],
[34.69128, 135.183087, 0.08128081180811808],
[34.685296, 135.832745, 0.04622546125461255],
[34.224806, 135.16795, 0.08866088560885609],
[35.503463, 134.238258, 0.051760516605166056],
[35.472248, 133.05083, 0.051760516605166056],
[34.66132, 133.934414, 0.060985608856088565],
[34.396033, 132.459595, 0.06836568265682658],
[34.185648, 131.470755, 0.051760516605166056],
[34.065732000000004, 134.559293, 0.051760516605166056],
[34.340140000000005, 134.04297, 0.051760516605166056],
[33.841649, 132.76585, 0.051760516605166056],
[33.55969, 133.530887, 0.051760516605166056],
[33.606767, 130.418228, 0.060985608856088565],
[33.249367, 130.298822, 0.05360553505535055],
[32.744541999999996, 129.873037, 0.10526605166051661],
[32.790385, 130.742345, 0.06652066420664207],
[33.2382, 131.612674, 0.05914059040590406],
[31.91109, 131.423855, 0.05729557195571956],
[31.560219, 130.557906, 0.0868158671586716],
[26.211538, 127.68111499999999, 0.07759077490774909]]
いざ描画
japan_map = folium.Map(location=[35, 135], zoom_start=6)
hm = plugins.HeatMapWithTime(heat_map_data, index=list(time_columns),auto_play=False,radius=30,max_opacity=1,gradient={0.1: 'blue', 0.25: 'lime', 0.5:'yellow',0.75: 'orange', 0.9:'red'})
hm.add_to(japan_map)
japan_map
約0.052以下がマイナスを取っているはずなので増分のみが可視化されています。
動く地図うれしい・・・!!!
ざつ目に考察
- 2010年くらいまではそんなに増加していない(やはり近年の増加傾向にある)
- 2010年以降は増加が見られるが大きい都市がある都道府県ばかり
- 地方はそんなに増えていない
終わり
そんなにコードを書かずに時系列ごとのデータの推移を可視化できるのはとても便利だと思いました。なんかもっと有意義なことに使いたいものです。最後までお読みいただきありがとうございました。訂正箇所とうあればコメントをしていただけると幸いです。