TOC
要旨
- folium を使うことで、Python で柔軟な地図可視化が出来る。しかもグリグリ動かせる
- folium は日本語チュートリアルが少ないので、簡単なものを網羅的にまとめる
- バージョンの更新で結構使い方が異なっているので、公式ドキュメント以外を参考すると上手くいかないことがある
概要
Folium は javascript ライブラリの leaflet を、Python で使えるようにしたモジュール。これを使うことで Python で柔軟な地図可視化の操作ができるようになります。
ただ、日本語版チュートリアルが少なく、またバージョンが 0.1 とか 0.2 の頃と現在とではマップにピンを置く関数名や使い方が大きく異なっています。余談だと、2017年時点では version0.5 だったのが、2018年に入ってから version0.6 となり、2018年11月に見たら version0.7 になっていました。
こういった事情で、公式ドキュメントを見る必要があるが、そうするとますます日本語ドキュメントの数が少なくなったり。そこで、簡単なものだけでも網羅的に使い方をまとめたいと思った次第です。
実装
Install
インストールは pip でも何でも。
# install if not installed $ pip install folium # upgrade if not latest edition $ pip install folium -U
Getting started
ここからのコードの実行は基本的には jupyter 上で行われていると想定してください。実行後、即表示されるのでオススメです。
そうでなければ、一度htmlで保存する必要があります。
import numpy as np import pandas as pd import folium from folium import plugins print( "folium version is {}".format(folium.__version__) )
folium version is 0.7.0 と出てきます。
使用するデータ
今回使用するデータは、Kaggle のリクルートコンペの hpg_store_info.csv です。緯度経度が入っていて、適当な大きさのデータだったためです。
Recruit Restaurant Visitor Forecasting | Kaggle
中身は、以下のような感じです。
hpg_store_id | hpg_genre_name | hpg_area_name | latitude | longitude | |
---|---|---|---|---|---|
0 | hpg_6622b62385aec8bf | Japanese style | Tōkyō-to Setagaya-ku Taishidō | 35.643675 | 139.668221 |
1 | hpg_e9e068dd49c5fa00 | Japanese style | Tōkyō-to Setagaya-ku Taishidō | 35.643675 | 139.668221 |
2 | hpg_2976f7acb4b3a3bc | Japanese style | Tōkyō-to Setagaya-ku Taishidō | 35.643675 | 139.668221 |
適当な1つの緯度経度地点が欲しいので、以下のように定義しておきます。
LAT = data.latitude.iloc[0] LNG = data.longitude.iloc[0]
地図を表示する
まずは、地図を表示させます。
そのためには、以下のようにするだけです。
m = folium.Map(location=[LAT, LNG]) m
location 引数は、表示する地図の中心点となります。
HTMLで保存
ちなみに、HTML 保存する場合も以下のように簡単にできます。
filename = 'output/map.html'
m.save(filename)
Tile
Tile とは地図のベースとなるものです。
デフォルトは Open Street Map というオープンライセンスのマップタイルが利用されます。
Tile については、説明するより見比べると理解しやすいと思います。
stamen-toner
m = folium.Map(location=[LAT, LNG], tiles="Stamen Toner", zoom_start=13) m
stamen-terrain
m = folium.Map(location=[LAT, LNG], zoom_start=12, tiles="Stamen Terrain") m
tile 引数で指定できます。かっこいい感じになりますね。
ちなみに、zoom_start 引数で最初のズームレベルを指定できます。こちらは指定しないと、「いい感じ」にしてくれるようです。ズームレベルは、1~20の整数値で指定し、1が最もズームアウトした状態、20が最もズームインした状態です。
あと、普段使いませんが、Mapbox も API Key を渡して使用することができるようです。
Markers
Simple Marker
最もシンプルなMarker。
ホバーやクリックで情報を表示する設定もできます。
m = folium.Map(location=[LAT, LNG]) folium.Marker( location=[data.latitude.iloc[100], data.longitude.iloc[100]], popup="This is Simple Marker", ).add_to(m) m
引数 popup で、クリックしたときに表示する情報を追加できます。
folium.Marker() でマーカーを作るだけでは地図に表示されず、これを add_to(m) することで、地図に情報を追加できます。このあたりの書き方が最初慣れず使いにくく感じました。
Marker with icon
工夫したい場合は、Marker の色を変えたり、アイコンを設定できたりできます。(Leaflet.R でいうところの AwesomeMarker です)
m = folium.Map(location=[LAT, LNG]) folium.Marker( location=[data.latitude.iloc[100], data.longitude.iloc[100]], popup=data.hpg_genre_name.iloc[100], tooltip="<i>" + data.hpg_area_name.iloc[100] + "</i>", # HTMLタグも使える icon=folium.Icon(color="red", icon="info-sign"), # 色やマーカーのアイコンを変更できる ).add_to(m) m
Circle and Circle Marker
ピンのマーカーではなく、円をマーカー代わりにできます。
m = folium.Map( location=[data.latitude.iloc[0], data.longitude.iloc[0]], tiles="Stamen Toner", zoom_start=10, ) folium.Circle( radius=500, location=[data.latitude.iloc[100], data.longitude.iloc[100]], popup="<b>" + data.hpg_genre_name.iloc[100] + "</b>", color="crimson", fill=False, ).add_to(m) folium.CircleMarker( location=[data.latitude.iloc[200], data.longitude.iloc[200]], radius=50, popup=data.hpg_genre_name.iloc[200], color="#3186cc", fill=True, fill_color="#3186cc", ).add_to(m) m
その他:lat/lng popovers
クリックした点の緯度経度をポップアップ表示します。
m = folium.Map(location=[46.1991, -122.1889], tiles="Stamen Terrain", zoom_start=13) m = m.add_child(folium.LatLngPopup()) m
その他:click-for-marker
クリックした点にMarkerを生成します。
m = folium.Map(location=[46.8527, -121.7649], tiles="Stamen Terrain", zoom_start=13) folium.Marker(location=[46.8354, -121.7325], popup="Camp Muir").add_to(m) m = m.add_child(folium.ClickForMarker(popup="Waypoint")) m
MarkerCluster
Leaflet.R と異なり、Folium.plugins.MarkerCluster で使える点に注意。ドキュメント探すときは、plugins の項目内にあるので探します。
folium.plugins.FeatureGroupSubGroup でクラスターをグループ分けできる
今までは、1点のみをマーカーを貼っていたが、複数の地点をマークしたい場合は、numpy の array 形式で渡してあげます。
locations = np.array([data.latitude, data.longitude]).T
m = folium.Map([LAT, LNG], zoom_start=4)
plugins.MarkerCluster(locations).add_to(m)
m
FeatureGroup
Creation, International cuisine, Japanese style の3つのジャンルがデータ中で最も多いジャンル名だったため、これらの地図を作成することにする。
def create_feature_group(genre): data_genre = data[data.hpg_genre_name == genre] arr_latlon = np.array([data_genre.latitude, data_genre.longitude]).T feature_group = folium.FeatureGroup(name=genre) plugins.MarkerCluster(arr_latlon).add_to(feature_group) return feature_group m = folium.Map(location=[LAT, LNG], zoom_start=12) genres = ["Creation", "International cuisine", "Japanese style"] for genre in genres: fg = create_feature_group(genre) fg.add_to(m) folium.LayerControl().add_to(m) m
DataFrame 内の全要素をプロットする
m = folium.Map(location=[LAT, LNG], zoom_start=10) data_nodup = data.drop_duplicates(subset=["latitude", "longitude"]) for lat, lon in zip(data_nodup.latitude, data_nodup.longitude): folium.Marker( location=[lat, lon], popup="/".join([str(lat), str(lon)]), tooltip=str(lat) + "_" + str(lon), ).add_to(m) m
その他
感覚的には、ポップアップをつけたMarkerを5000以上プロットしようとすると、固まるイメージ。 Vincent/Vega and Altair/VegaLite Markers を使用する際も、渡す地図情報のサイズを重すぎないようにすることが大事かも。 (具体的にどれくらいメモリを使っているのか、どのように処理が進んでいるのかは調べていない)
LeafletRでは、マップオブジェクトにパイプ( %>%
)で付加情報を足していって地図を生成する Folium の場合だと、Mapオブジェクトをはじめに定義して、その後各種要素を作って add_to(m)
していく
ただ、クラスターに関しては、LeafletR ではaddMarkerの引数でオプションを指定したが、 Folium ではMarkerCluster オブジェクトを別で作成して add_to(m)
する必要がある
LeafletR は、kazutan さんのサイトを参照するのがいいと思います