こんな悩みにお答えします。
>>【Python】位置情報(緯度・経度)を可視化する方法(答えはfoliumライブラリ)
>>【Python】foliumのマーカーアイコンを自由に変更する方法
1. Aidemy
・・・AIエンジニアとしてのスキルを身に付けたい。その延長で就職、転職できたら嬉しい方におすすめ。
2.侍エンジニア塾
・・・基礎力と即戦力を身に付けられる、満足度No.1スクール
3. TechAcademy
・・・副業に向けてスキルアップしたい。フリーランスなどを目指している方向け。
4. AIジョブカレッジ
・・・コスパ良く、質の高い講座を受けたい方におすすめ。
本記事の信頼性
現在はデータサイエンティストとして大企業で活動しています。
1.
位置情報の時系列データを可視化する方法は大きく2種類あります。
それぞれ少しずつ用途が異なるため、あなたのユースケースに合わせて参考いただければと思います。
完成イメージは以下のとおりです。
2.実行環境
以降、実際にコーディングしていく上で、実行環境を記載いたします。
- Python:3.6.4
- folium:0.12.1
- selenium:3.141.0
- PIL:8.3.1
3.
まずはfoliumメソッドを利用してヒートマップを作成する方法です。
a. 方法と特徴
ヒートマップを作成する場合、folium.plugins.HeatMapWithTime()を利用します。データと時間を指定することで簡単にヒートマップで可視化することができます。
foliumのメソッドを利用する方法の特徴は
- 円のサイズやスケール(ヒートマップの色度合い)を自由に変更できる
- foliumのメソッドであるため容易に実装できる
などが挙げられます。
一方で、データ形式に特徴があるため前処理の段階でデータを整形してあげる必要があります(詳細は次項)。
b. サンプルデータ
今回は簡単のため、サンプルデータは手動で作成しております。
data = [
[[35.660126,139.697719,0.3]],
[[35.659012,139.703387,0.5]],
[[35.658034,139.701636,0.8]],
[[35.657034,139.69636,0.95]]
]
ヒートマップを作成する際のデータ形式は上記のような3次元データです。内訳は以下のとおり。
各次元データ | 概要 |
表示する時間 | 時系列分のデータを格納します。上記例では4時刻分データがあり可視化した際に4段階の移動が表示されます。 |
特定時刻におけるデータ | 上記例では各時刻とも1つのデータしか格納しておりませんが、データ数に応じて複数のデータが格納されます。 |
データ(緯度、経度、スケール) | 緯度・経度は必須、スケールは任意になります。またスケールはヒートマップの色合いに影響するため0~1に正規化しておく必要があります。 |
c. サンプルコード
import folium
from folium import plugins
import datetime
#ヒートマップを作成する関数
def map_view(data,time_index,map_file="default"):
#ベースとなるマップを作成。渋谷近辺
map = folium.Map([35.658034, 139.701636], zoom_start=15,tiles="Stamen Terrain")
hm = plugins.HeatMapWithTime( #ヒートマップを作成
data,
index=time_index, #時刻指定
auto_play=True,
radius=30, #ヒートマップの大きさ
max_opacity=0.3,
gradient={0.1: 'blue', 0.25: 'lime', 0.5:'yellow',0.75: 'orange', 0.9:'red'}#色度合い指定
)
hm.add_to(map)
map_file = map_file + ".html"
map.save(map_file) #作成したマップを保存
#サンプルデータ
data = [
[[35.660126,139.697719,0.3]],
[[35.659012,139.703387,0.5]],
[[35.658034,139.701636,0.8]],
[[35.657034,139.69636,0.95]]
]
#表示する時刻(サンプルデータのデータ数と一致している必要あり)
time_index = [
datetime.time(20, 31, 0, 0).strftime("%H:%M:%S"),
datetime.time(20, 38, 0, 0).strftime("%H:%M:%S"),
datetime.time(20, 40, 0, 0).strftime("%H:%M:%S"),
datetime.time(21, 26, 0, 0).strftime("%H:%M:%S")
]
#heatmap_1として可視化・保存
map_view(data,time_index,map_file="heatmap_1")
こちらのコードを実行した結果は以下のとおり。
4つのデータの時間推移が確認でき、時刻も指定した通りに記載されています。ヒートマップ中の繰り返しボタンを押した上で再生ボタンを押下すると連続して時間推移を確認することができます。
また、以下のように緯度、経度のみのデータを用意した場合も実行してみます。
data = [
[[35.660126,139.697719]],
[[35.659012,139.703387]],
[[35.658034,139.701636]],
[[35.657034,139.69636]]
]
結果は以下のとおり。色合いがすべて統一されていることが分かります。あなたの用途次第ではスケールを用意しなくても十分というケースもあると思います。あなたの目的に合わせて実行してみてください。
4.
次に各時刻ごとに画像を作成し繋ぎ合わせることでGIFを作成する方法です。
a. 方法と特徴
この方法では、以下の流れでGIF化していきます。
- folium.Marker()を利用してマーカーをプロットし特定時刻におけるマップを作成
- これを全時刻分繰り返し、それぞれhtmlファイルを作成
- htmlファイルをスクリーンショットしpngファイルを作成
- pngファイルを繋ぎ合わせGIF作成
特徴としては、folium.Marker()を利用するためマーカーのアイコンを自由に変更できることです。したがって、特定の人物の移動軌跡を作成することができるようになります。
一方で、処理工程が多く複雑のため作業時間がかかってしまうことが難点です。
今回はこちらの記事を参考にさせていただきました。
b. サンプルデータ
ここでは特定人物の移動を可視化するために以下のようなExcelファイル(sample.xlsx)を用意しました。ヒートマップとの差分としてはスケールを記載していない部分が大きいかと思います。
time name lat lon
2018/10/31,20:31:00 gojo 35.660126 139.697719
2018/10/31,20:38:00 gojo 35.659012 139.703387
2018/10/31,20:40:00 gojo 35.658034 139.701636
2018/10/31,21:26:00 gojo 35.657034 139.69636
例のごとく、「呪術廻戦」より五条先生にご協力いただいております。
c. サンプルコード
処理ごとに記述していきます。
c-1. 特定時刻におけるマップを全時刻分作成
import folium
import datetime
from folium.features import CustomIcon
from IPython.display import Image
import pandas as pd
#ベースとなるマップを作成する関数
def base_map():
map = folium.Map(
[35.66256010486336, 139.7016042283173],
tiles="Stamen Terrain",
zoom_start=15
)
return map
#アイコンを作成する関数
def get_image(img_name):
icon = CustomIcon(
icon_image = img_name,
icon_size = (55, 65),
icon_anchor = (30, 30),
popup_anchor = (3, 3)
)
return icon
#マップを保存する関数
def save_map(map,map_file="default"):
map_file = map_file + ".html"
map.save(map_file)
#アイコンを表示する関数
def character_view(map,img_name,icon_latlon,popup):
icon = get_image(img_name)
folium.Marker(location = icon_latlon,icon = icon,popup=popup).add_to(map)
#特定時刻のマップを作成し保存する関数
def specific_map_view(data,map,start,end,span):
forcus_time_st = start
forcus_time_ed = start + span
k = 1
while True:
forcus_data = data[data['time'] < forcus_time_ed]
forcus_data = forcus_data[forcus_data['time'] >= forcus_time_st]
if len(forcus_data) >=1:
for i in range(len(forcus_data)):
img_name = "./pic/" + str(forcus_data[i:i+1]["name"].values[0]) + ".png"
icon_latlon = forcus_data[i:i+1][["lat","lon"]].values.reshape(-1).tolist()
character_view(map,img_name,icon_latlon,forcus_data[i:i+1]["name"].values[0])
save_map(
map,
map_file="./pic/default_"+str(forcus_time_st.hour)+"_"+str(forcus_time_st.minute)+"_sample"
)
forcus_time_st = forcus_time_ed
forcus_time_ed = forcus_time_ed + span
map = base_map()
k += 1
if forcus_time_ed > end:
break
if __name__ == '__main__':
try:
latlon = pd.read_excel(
'./sample.xlsx',
header=0,
index_col=None,
engine='openpyxl'
)
latlon['time'] = pd.to_datetime(latlon['time'], format="%Y/%m/%d,%H:%M:%S")
map = base_map()
start = datetime.datetime(2018, 10, 31, 20, 30, 0, 0)
end = datetime.datetime(2018, 10, 31, 21, 40, 0, 0)
span = datetime.timedelta(minutes=5)
specific_map_view(latlon,map,start,end,span)
except Exception as e:
print(e)
汎用性の高い形にするために、時間を区切ってマップを作成できるように設計しています。
上記プログラムを実行した結果4つのファイルが作成されます(20_30は20:30~20:35までの時間幅を表しています)。
- default_20_30.html
- default_20_35.html
- default_20_40.html
- default_21_25.html
一例としてdefault_20_30.htmlの結果を確認すると以下のとおりです。
c-2. htmlファイルのスクリーンショットを取りpngファイル作成
続いて、作成した4つのhtmlファイルのスクリーンショットを取っていきます。ここではseleniumライブラリのwebdriverを使用します。
webdriverを用いることでPython上でブラウザを操作できるようになります。詳細は割愛しますが、pipでインストール可能です。
$pip install selenium
サンプルコードは以下のとおりです。
import os
import glob
from selenium import webdriver
from datetime import date
import datetime
import time
files = glob.glob("./pic/sample/*") #作成したhtmlファイル名一覧を取得
driver_path = "./chromedriver.exe"
delay=5
for file in files:
file = file[13:]
tmpurl='file://{path}/pic/sample/{mapfile}'.format(path=os.getcwd(),mapfile=file)
driver = webdriver.Chrome(driver_path) #Chromeでhtmlファイルを操作
driver.get(tmpurl)
time.sleep(delay)
save = "./pic/sample/png/" + str(file[:-5]) + ".png"
driver.save_screenshot(save) #スクリーンショットを取り、pngファイルとして保存
driver.quit()
文字列の扱いなどは置換操作を使えばもっとスマートに記述できます。ここでは汚いコードになっています。。
c-3. pngファイルを繋ぎ合わせてGIFファイル作成
最後にGIF化を行っていきます。サンプルコードはこちら。
from PIL import Image, ImageDraw, ImageFont
from datetime import datetime, date, timedelta
import glob
import pandas as pd
im = []
files = glob.glob("./pic/sample/png/*") #ファイル名一覧を取得
#Excelファイルから時刻を取得
latlon = pd.read_excel('./sample.xlsx', header=0, index_col=None,engine='openpyxl')
latlon['time'] = pd.to_datetime(latlon['time'], format="%Y/%m/%d,%H:%M:%S")
#1ファイルずつ追加し、時刻入力
for i in range(len(files)):
img = Image.open(files[i])
draw = ImageDraw.Draw(img)
#フォントはカレントディレクトリに格納
font = ImageFont.truetype("FontsFree-Net-arial-bold.ttf", 70) #70はフォントサイズ
text =str(latlon['time'][i].year)+"/"+
str(latlon['time'][i].month)+"/"+
str(latlon['time'][i].day)+" "+
str(latlon['time'][i].hour)+":"+
str(latlon['time'][i].minute)
draw.text((30, 30),str(text),font=font,fill=(0,0,0))
im.append(img)
#全てのpngファイルを繋げてGIF化
im[0].save(
'./pic/sample/png/sample.gif',
save_all=True,
append_images=im[1:],
optimize=False,
duration=1500, #各フレームの表示時間(ミリ秒)
loop=0
)
PILを用いて複数のpngファイルからGIFを作成しています。また、時刻を記述するにあたり、こちらよりフォントをダウンロードさせていただきました。
完成したGIFはこちら。五条先生が時刻変化に合わせて移動していることが確認できます。
5. まとめ
今回は、
しました。時系列位置情報を可視化し時間推移を見る方法はとても汎用性が高いです。
位置ごとの店舗の売上高や新型コロナウイルスの新規感染者数などはヒートマップ。散策コースの可視化や軌跡推移はGIF化によって実現することができます。
Pythonで人生を豊かにしていきましょう。
・【株式投資】Pythonでスクリーニングする方法
・【Python】【株式投資】TA-Libによるテクニカル指標算出方法(移動平均、ボリンジャーバンド、MACD、RSI)
・【初心者向け】20分でできる!Pythonで銘柄スクリーニング結果をスマホへ通知する方法
・【Python】銘柄スクリーニング結果を定期的に通知する方法(無料)【30分でできる!】
本ブログでは、株式やプログラミングに関する記事を投稿しています。
プログラミング(Python)を学びたい方におすすめの書籍やプログラミングスクール、おすすめの学習方法などをご紹介しておりますのでぜひご覧ください。
は、プログラミングスクールという方法がおすすめです。
私の一押しは『TechAcademy』です。
質問することですぐに分からないところをクリアにできますし、進捗をサポートしてくれるため確実に成長することができます。
無料相談を実施しているため、まずは話を聞いてあなたのスタイルに合っているかどうか確認してみるのが良いと思います。
コメント