Python の 地図描画ライブラリを使おうと思い、javascript で吐き出せて マウスでぐりぐりできる folium
を使うことにしました。
folium
を使う前提で、地図にプロットできそうな統計データを探していたのですが、国勢調査のデータの中に 夫の年齢(各歳),妻の年齢(各歳)別夫婦数(総数及び日本人) - 全国※,全国市部・郡部,都道府県,21大都市
という興味深いデータを見つけました。
統計表一覧 政府統計の総合窓口 GL08020103
このデータを加工して folium の地図上にプロットしてみようかと思います。
参考
-
統計表一覧 政府統計の総合窓口 GL08020103
国勢調査の各種データのダウンロードリンク、何かに使えそうなデータが多数あります。
使い方についてはアルコールが入っていない状態で真面目に考えたいと思いました。 -
Pythonのfoliumパッケージで地図を描いてみる - Qiita
folium のインストール方法、基本的な使い方が記載されています。 -
Python pandas + folium + Jupyter でリーフレット / コロプレス図を描きたい - StatsFragments
今回描きたいコロプレス図の描き方について記載されています。 -
【みんなの知識 ちょっと便利帳】都道府県庁所在地 緯度経度データ - 各都市からの方位地図 - 10進数/60進数での座標・世界測地系(WGS84)
都道府県の県庁所在地の緯度、経度のリストがダウンロードできます。
前提
以下の環境で作業は実施しています。
-
OS
% sw_vers ProductName: Mac OS X ProductVersion: 10.12.6 BuildVersion: 16G29
-
pythonのverion
% python -V Python 2.7.10
-
使用したfolium のversion
% pip list | grep folium folium (0.3.0)
folium インストール
pip でインストールします。
pandas も必要なので、pandas も。
pip install pandas
pip install folium
実施作業の流れ
以下の通り作業は実施していきました。
時系列順に記載します。
-
GeoJSON
ファイルの準備 -
夫の年齢(各歳),妻の年齢(各歳)別夫婦数(総数及び日本人) - 全国※,全国市部・郡部,都道府県,21大都市
データの加工 -
コロプレス図を描画
-
コロプレス図にポップアップを追加する
作成したスクリプトはkemsakurai/folium_example にUPしました。
コロプレス図にポップアップを追加する
スクリプトについては、Githubを参照ください。
GeoJSON
ファイルの準備
コロプレス図を描くには、GeoJSON または、TopoJSON 形式のファイルが必要になります。
夫の年齢(各歳),妻の年齢(各歳)別夫婦数(総数及び日本人) - 全国※,全国市部・郡部,都道府県,21大都市
は都道府県を 色分けできればうまく描画できそうですので、都道府県のGeoJSON
データを探してみました。
探したところ以下、見つかりました。 land/japan.geojson at master · dataofjapan/land
以下、curl コマンドでファイルをダウンロードし、地図上に描画してみます。(サイズが大きいって思いましたが、geojsonのサイズはこういうものなのかもしれません)
-
curlコマンド
curl https://raw.githubusercontent.com/dataofjapan/land/master/japan.geojson > japan.geojson
-
plot_map.py
動作確認用に以下 python スクリプトを作成しました。
# -*- coding: utf-8 - import folium def main(): # 地図の基準として兵庫県明石市を設定 japan_location = [35, 135] m = folium.Map(location=japan_location, zoom_start=5) geojson = r'japan.geojson' # geojson読み込み m.choropleth(geo_data=geojson) # 地図をhtml形式で出力 m.save(outfile="map.html") if __name__ == "__main__": main()
-
実装の説明
geo_json
メソッドは、folium (0.3.0)
だと対象のメソッドがなくなったらしく、以下エラーとなりました。
どうも、python-visualization/folium: Python Data. Leaflet.js Maps. のAttributeError: 'Map' object has no attribute 'geo_json'
README.md
に記載のあるchoropleth
にメソッドが置き換わったようです。 -
出力されるHTML
上記スクリプトを実行すると、以下のような地図が出力されます。
実際は世界地図が表示されますが、日本のみ切り取ってます。
GeoJSON
の読み込み確認は以上です。
夫の年齢(各歳),妻の年齢(各歳)別夫婦数(総数及び日本人) - 全国※,全国市部・郡部,都道府県,21大都市
データの加工
夫の年齢(各歳),妻の年齢(各歳)別夫婦数(総数及び日本人) - 全国※,全国市部・郡部,都道府県,21大都市
は、CSV形式、DB形式でのダウンロードができます。
DB形式の使い方がわからず、CSVをダウンロードしたのですが、通常のCSVの形式ではないため、多少加工が必要です。
元のデータに対して以下のような加工を施しました。
-
不要なヘッダ部の削除
-
全国、市のデータを削除し、都道府県データのみにする
-
-
を0
に置換 -
各都道府県ごとに、
夫婦の年齢差合計
を計算 (夫の年齢合計 - 妻の年齢合計) -
夫婦の年齢差合計
を各都道府県の総夫婦数で割る -
各都道府県の地域コードを
japan.geojson
と突き合わせ可能な形式に変換
上記手順で時間にして1時間程度かかりました。
編集した結果をcsvにして、pandas
で読み込みと以下の通り表示されます。
一言で言うと各県でそれほど夫婦の年齢差に開きはなく、だいたい夫のほうが2歳年上です。
- CSVの読み込み結果
>>> import pandas as pd >>> pd.read_csv('fuhu_seikei.csv') Area Code Area Name Ave Sum 0 1 北海道 2.252136 2850398 1 2 青森県 2.572007 781319 2 3 岩手県 2.451050 747188 3 4 宮城県 2.312749 1255284 4 5 秋田県 2.502581 636549 5 6 山形県 2.369089 671118 6 7 福島県 2.309119 1051790 7 8 茨城県 2.377381 1700355 8 9 栃木県 2.296028 1110933 9 10 群馬県 2.208948 1059895 10 11 埼玉県 2.362114 4159938 11 12 千葉県 2.409113 3622084 12 13 東京都 2.403701 6906918 13 14 神奈川県 2.389190 5160165 14 15 新潟県 2.284645 1302798 15 16 富山県 2.622729 700990 16 17 石川県 2.578889 716160 17 18 福井県 2.634202 513435 18 19 山梨県 2.488761 503541 19 20 長野県 2.442753 1283469 20 21 岐阜県 2.662840 1365110 21 22 静岡県 2.530297 2307603 22 23 愛知県 2.561806 4576349 23 24 三重県 2.617312 1180811 24 25 滋賀県 2.546597 886692 25 26 京都府 2.478029 1473632 26 27 大阪府 2.363501 4681998 27 28 兵庫県 2.467564 3271282 28 29 奈良県 2.538160 867962 29 30 和歌山県 2.598260 610695 30 31 鳥取県 2.373060 320878 31 32 島根県 2.322820 391934 32 33 岡山県 2.397910 1092344 33 34 広島県 2.389589 1624244 34 35 山口県 2.512688 847326 35 36 徳島県 2.399621 432966 36 37 香川県 2.366874 558613 37 38 愛媛県 2.367458 778922 38 39 高知県 2.432469 399066 39 40 福岡県 2.235163 2537348 40 41 佐賀県 2.322415 452922 41 42 長崎県 2.265973 727314 42 43 熊本県 2.279736 956087 43 44 大分県 2.339118 649035 44 45 宮崎県 2.225000 580981 45 46 鹿児島県 2.358946 900669 46 47 沖縄県 2.122128 612737 >>>
コロプレス図を描画
作成したcsvをinputにして、コロプレス図の描画します。
都道府県別の夫婦の年齢差の平均値
を元に色が変わるようにしています。
* plot_map.py
# -*- coding: utf-8 -
import folium
import pandas as pd
def main():
# 地図の基準として兵庫県明石市を設定
japan_location = [35, 135]
m = folium.Map(location=japan_location, zoom_start=5)
geojson = r'japan.geojson'
# CSVデータの読み込み
df = pd.read_csv('fuhu_seikei.csv')
m.choropleth(geo_data=geojson, data=df,
columns=['Area Code', 'Ave'],
key_on='feature.properties.id',
threshold_scale=[2.2, 2.3, 2.4, 2.5, 2.6, 2.7],
fill_color='YlGnBu', reset=True)
# 地図をhtml形式で出力
m.save(outfile="map.html")
if __name__ == "__main__":
main()
- 出力されるHTML
上記スクリプトを実行すると、以下のような地図が出力されます。
理由は不明ですが、中部地方が年齢差が大きいようです。
ポップアップを追加する
コードが長いため、地図、と説明のみ記載します。
- 出力されるHTML
説明
アイコンを配置したら、あまりいい感じになりませんでしたが、実装について説明します。
MulitiPoligonのGeoJsonのアイコン配置について
少なくともfolium側でいい感じに配置してくれる機能は現状なさそうだったので、県庁所在地の位置に対してpopup
を表示するようにしました。
県庁所在地の緯度経度は、【みんなの知識 ちょっと便利帳】都道府県庁所在地 緯度経度データ - 各都市からの方位地図 - 10進数/60進数での座標・世界測地系(WGS84) を拝借し辞書を作成、それを使う形にしました。
ポップアップマーカについて
マーカーが幾つか用意されています。python-visualization/folium: Python Data. Leaflet.js Maps. のREADME.md
から設定できるもので設定をしてみました。コメントアウト、初期状態でのコメントアウト部を解除してもらえば、RegularPolygonMarker
、CircleMarker
でマーカをプロットできます。
マーカのパラメータはちょっと気合いを入れて調べないとわからなそうで、途中で断念していますので調整の余地ありかと思います。
key_on の設定について
ここは直感的にわからなかったのでを記載しておきます。
Geojson 上に feature
はキー値として存在しませんが、features
は存在しており、且つ、配列です。
key_on
の指定がfeature.properties.id
なので、features
> 1要素 feature
としての解釈は folium 側でうまいことやってくれています。
今回 id としたいのは地域コードで、properties
の下にぶらさがっており、on_key
に feature.properties.id
を指定しています。
- folium_example/japan.geojson at master · kemsakurai/folium_example
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "nam": "Kyoto Fu", "nam_ja": "京都府", "id": 26 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 135.036697387695, 35.537334442138686 ], [ 135.035202026367, 35.53973388671878 ], [ 135.03010559082, 35.53986740112302 ], .........
説明は以上です。
作業中発生したエラーについて
作業中に発生したエラーについてまとめます。同じエラーが発生した場合はご活用ください。
-
threshold_scale の要素数を7以上にした
ValueError
が発生します。
エラー発生箇所を確認したところ、確かに要素数の判定をしていたので、要素数6にしました。File "/Library/Python/2.7/site-packages/folium/folium.py", line 247, in choropleth raise ValueError
if threshold_scale and len(threshold_scale) > 6: raise ValueError
-
pandasキー値指定のスペルミス
pandas のキー値指定のスペルミスで上記エラーが発生しました。Traceback (most recent call last): File "plot_map.py", line 23, in <module> main() File "plot_map.py", line 18, in main fill_color='BuPu') File "/Library/Python/2.7/site-packages/folium/folium.py", line 263, in choropleth color_data = data.set_index(columns[0])[columns[1]].to_dict() File "/Library/Python/2.7/site-packages/pandas/core/frame.py", line 2830, in set_index level = frame[col]._values File "/Library/Python/2.7/site-packages/pandas/core/frame.py", line 1964, in __getitem__ return self._getitem_column(key) File "/Library/Python/2.7/site-packages/pandas/core/frame.py", line 1971, in _getitem_column return self._get_item_cache(key) File "/Library/Python/2.7/site-packages/pandas/core/generic.py", line 1645, in _get_item_cache values = self._data.get(item) File "/Library/Python/2.7/site-packages/pandas/core/internals.py", line 3590, in get loc = self.items.get_loc(item) File "/Library/Python/2.7/site-packages/pandas/core/indexes/base.py", line 2444, in get_loc return self._engine.get_loc(self._maybe_cast_indexer(key)) File "pandas/_libs/index.pyx", line 132, in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5280) File "pandas/_libs/index.pyx", line 154, in pandas._libs.index.IndexEngine.get_loc (pandas/_libs/index.c:5126) File "pandas/_libs/hashtable_class_helper.pxi", line 1210, in pandas._libs.hashtable.PyObjectHashTable.get_item (pandas/_libs/hashtable.c:20523) File "pandas/_libs/hashtable_class_helper.pxi", line 1218, in pandas._libs.hashtable.PyObjectHashTable.get_item (pandas/_libs/hashtable.c:20477) KeyError: 'Area code'
スペルミスを直せば正常に動作します。 -
on_key指定のキー値のスペルミス
on_key のスペルミスで発生するエラーです。Traceback (most recent call last): File "plot_map.py", line 25, in <module> main() File "plot_map.py", line 22, in main m.save(outfile="map.html") File "/Library/Python/2.7/site-packages/branca/element.py", line 157, in save html = root.render(**kwargs) File "/Library/Python/2.7/site-packages/branca/element.py", line 301, in render child.render(**kwargs) File "/Library/Python/2.7/site-packages/folium/map.py", line 299, in render super(LegacyMap, self).render(**kwargs) File "/Library/Python/2.7/site-packages/branca/element.py", line 617, in render element.render(**kwargs) File "/Library/Python/2.7/site-packages/branca/element.py", line 613, in render figure.script.add_child(Element(script(self, kwargs)), File "/Library/Python/2.7/site-packages/jinja2/runtime.py", line 432, in __call__ return self._func(*arguments) File "<template>", line 32, in macro File "/Library/Python/2.7/site-packages/jinja2/runtime.py", line 193, in call return __obj(*args, **kwargs) File "/Library/Python/2.7/site-packages/folium/features.py", line 409, in style_data feature.setdefault('properties', {}).setdefault('style', {}).update(self.style_function(feature)) # noqa File "/Library/Python/2.7/site-packages/folium/folium.py", line 316, in style_function "fillColor": color_scale_fun(x) File "/Library/Python/2.7/site-packages/folium/folium.py", line 304, in color_scale_fun get_by_key(x, key_on) in color_data and File "/Library/Python/2.7/site-packages/folium/folium.py", line 299, in get_by_key '.'.join(key.split('.')[1:]))) File "/Library/Python/2.7/site-packages/folium/folium.py", line 298, in get_by_key get_by_key(obj.get(key.split('.')[0], None), AttributeError: 'NoneType' object has no attribute 'get'
スペルミスが多すぎます。 -
TypeError: choropleth() got an unexpected keyword argument ‘geo_path’
この記事を書いている最中に、version (0.4.0) がリリースされました。
version (0.3.0) だと、と書けますが、 version (0.4.0) だと、m.choropleth(geo_path=geojson)
と書くようになりました。m.choropleth(geo_data=geojson)
-
Markerの追加でwarning
以下のwarningが発生しました。
Map に マーカをaddする箇所で、folium の過去 version だと、plot_map.py:92: FutureWarning: Method `add_children` is deprecated. Please use `add_child` instead. m.add_children(marker)
add_children
と書いていましたが、
非推奨になって、同じ役割をするadd_child
が作成されたため発生しています。
単純にadd_child
にメソッドを変更すれば解消されます。
まとめ
政府統計 のデータと、folium を使って コロプレス図を描画しました。
以下、まとめます。
-
ぱっと見でおもしろいと思ったデータが加工しておもしろくなるとは限らない
平均値をとったあたりで、おもしろさがなくなっていったのかもしれません。
平均化したら、都道府県ごとでそれほど大きい違いはありませんでした。
主観的なバイアスをかけたりすると誇張された感じにはなるかもですが、マナー違反なきはします。
ただ、政府統計データ自体は数も多く、「何故、この観点で調べたのか経緯は全くわからないが個人的にはおもしろい」と感じるデータが多く、まさぐってみたくなりました。 -
folium自体は何でもできるわけではないが使いやすい
popup を出力したり、onclick アクションを追加したり、凝ったことをしようとすると、詰むか詰まないかのところで試行することになりましたが、
コロプレス図自体は、Webの情報やpython-visualization/folium: Python Data. Leaflet.js Maps. のREADME.md
を参考にすれば、時間をかけることなく望んだものが出力できました。
凝ったことをする場合は、Leaflet - a JavaScript library for interactive maps 側のことも意識しないといけないと思いますが、
基本的なところは、folium
でだいたいできると感じました。
以上です。
コメント