宙畑 Sorabatake

Tellusの使い方

【ゼロからのTellusの使い方】Jupyter Labで位置情報データ「Profile Passport」を取得する

先日リリースされたTellusバージョン1.1で追加された位置情報データ「Profile Passport」を取得する方法をご紹介いたします。

記事作成時から、Tellusからデータを検索・取得するAPIが変更になっております。該当箇所のコードについては、以下のリンクをご参照ください。
https://www.tellusxdp.com/ja/howtouse/access/traveler_api_20220310_
firstpart.html

2022年8月31日以降、Tellus OSでのデータの閲覧方法など使い方が一部変更になっております。新しいTellus OSの基本操作は以下のリンクをご参照ください。
https://www.tellusxdp.com/ja/howtouse/tellus_os/start_tellus_os.html

本記事ではTellusのJupyter Labを使って位置情報データ「Profile Passport」を取得する方法を紹介します。
TellusでJupyter Lab(Jupyter Notebook)を使う方法は、「Tellusの開発環境でAPI引っ張ってみた」をご覧ください。

1. 位置情報データ「Profile Passport」を取得するには

「Profile Passport」とは、ブログウォッチャー社が提供する独自開発SDKによるスマートフォンの位置情報データです。

東京近郊に限られますが、2016年7月から2017年9月までの期間の「Profile Passport」のデータを、Tellus上では利用することができます。

※画像を利用する際は、データ詳細のデータポリシーを確認して、規約を違反しないように注意してください。

Tellus OS上ではヒートマップとして利用できます。

人の多さをヒートマップで可視化しています

Tellus IDE(開発環境)からはAPIを使ってデータを取得することができます。

https://pflow.tellusxdp.com/api/v1/profile_passport/get_count{?begin_time,end_time,interval,bouds}

引数

begin_time 必須str 開始時刻(世界標準時)
例:’2017-01-01 09:00:00’
end_time 必須int 終了時刻(世界標準時)
例:’2017-01-01 10:59:59’
interval 必須str 時間頻度(分)
例:60(30 or 60)
bounds 必須str 取得する地理的範囲
’最小緯度,最小経度,最大緯度,最大経度’で指定する。
例:’-90.0,-180.0,90.0,180.0’

※期間や地域を広く指定すると取得するデータが多くなり504エラーが出る場合があります。

詳しくはAPIリファレンスの「Tellus 人流 API」を参照してください。

ではこれをJupyter Lab上で呼び出してみましょう。

Tellus IDEを開き、「work」ディレクトリに移動してから「Python3」を選択し、以下のコードを貼り付けます。

「work」ディレクトリに移動(Jupyter Labの画面例)
「Python3」のパネルを選択

import os, json, requests
from io import BytesIO
from datetime import datetime
from datetime import timedelta
from dateutil import relativedelta

TOKEN = 'ここには自分のアカウントのトークンを貼り付ける'
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
INTERVAL = 60

def get_profile_passport_count(begin_time, end_time, interval, bounds):
    """
    /api/v1/profile_passport/get_count

    Parameters
    ----------
    begin_time : str 
        取得開始時刻 UTC(yyyy-mm-dd HH:MM:SS) 
    end_time : str 
        取得終了時刻 UTC(yyyy-mm-dd HH:MM:SS) 
    interval : number 
        取得間隔(30 or 60)
    bbox : str 
        取得範囲(最小緯度,最小経度,最大緯度,最大経度)
    Returns
    -------
    content : list
        結果
    """

    url = 'https://pflow.tellusxdp.com/api/v1/profile_passport/get_count'
    headers = {
        'Authorization': 'Bearer ' + TOKEN
    }
    payload = {
        'begin_time': begin_time,
        'end_time': end_time,
        'interval': interval,
        'bounds': bounds
    }

    r = requests.get(url, headers=headers, params=payload)

    if r.status_code is not 200:
        raise ValueError('status error({}).'.format(r.status_code))
    return json.loads(r.content) 

def get_profile_passport_count_per_hour(begin_year, begin_month, begin_day, begin_hour, bbox, days=0, hours=0):
    """
    1時間ごとのprofile_passportの結果を取得する。

    Parameters
    ----------
    begin_year : int
        取得する年(日本標準時)
    begin_month : int
        取得する月(日本標準時)
    begin_day : int
        取得する日(日本標準時)
    begin_hour : int
        取得する時間(日本標準時)
    begin_bbox : array_like 
        取得する領域のBounding Box
    days : int
        何日後まで取得するか
    hours : int
        何時間後まで取得するか(0 ~ 23)

    Returns
    -------
    data : list
        結果
    """
    # APIには世界標準時で渡す
    begin_datetime = datetime(begin_year, begin_month, begin_day, begin_hour, 0, 0, 0) - timedelta(hours=9)
    end_datetime = begin_datetime + timedelta(days=days,hours=hours)
    bounds = str(bbox[1])+","+str(bbox[0])+","+str(bbox[3])+","+str(bbox[2])

    try:
        data = get_profile_passport_count(begin_datetime.strftime(TIME_FORMAT), end_datetime.strftime(TIME_FORMAT), INTERVAL, bounds)
    except ValueError as e:
        print(e)
    return data
        
shinjuku_bbox = [139.687011, 35.683024, 139.700284, 35.692078]
data_shinjuku_20170401 = get_profile_passport_count_per_hour(2017, 4, 1, 12, shinjuku_bbox, hours=1)

print(len(data_shinjuku_20170401))
print(data_shinjuku_20170401)

トークンはマイページのAPIアクセス設定(要ログイン)から取得してください。

上部の三角形アイコンをクリックしてコードを実行すると、指定した範囲の位置情報データが返ってきます。

取得できる位置情報データ

begin_time 集計開始時刻(世界標準時)
end_time 集計終了時刻(世界標準時)
mesh 集計範囲の地域メッシュコード(4分の1地域メッシュ)
count 人数
※ブログウォッチャー社が計測した数値であり、実際のメッシュ内の総数を示すものではありません。

サンプルコードでは、2017年4月1日12時から13時の新宿西口付近のデータを取得しています。

2017年4月1日12時から13時の新宿西口付近(16メッシュ分)のデータが返ってきました。(出典:ブログウォッチャー)

結果より抜粋

{
    'begin_time': '2017-04-01 03:00:00', 
    'end_time': '2017-04-01 03:59:59', 
    'mesh': '5339452511',
    'count': 160
}

begin_timeとend_timeは集計時刻です。このデータは2017年4月1日の03:00:00から03:59:59の間(世界標準時)の値です。データは日本時間と9時間ずれた世界標準時で返ってくることに注意してください。

meshは地域メッシュコードを表します

地域メッシュコードとは、日本工業規格で定められた区域を識別するコードのことで、このAPIでは4分の1地域メッシュ区分で値が返却されます。
地域メッシュについて詳しくはこちらを参考にしてください。

2. 1日の人の動きをグラフ化しよう

ある地点にいる人のおおよその数を取得できるようになったので、1日の間にメッシュ内に滞在する人の数がどれだけ変化するか調べてみましょう。

ginza_bbox = [139.765034, 35.668432, 139.769242, 35.671083]
data_ginza_20170401 = get_profile_passport_count_per_hour(2017, 4, 1, 0, ginza_bbox, days=1)

print(data_ginza_20170401[0:3])
(出典:ブログウォッチャー)

サンプルコードでは2017年4月1日の銀座駅周辺(メッシュコード:5339460114)のデータを1時間毎に取得しています。

では取得したデータをグラフ化してみましょう。

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
%matplotlib inline

def plot_daily_count(data, year, month, day, mesh):
    """
    1日分のprofile_passportの結果をグラフ化

    Parameters
    ----------
    year : int
        取得する年(日本標準時)
    month : int
        取得する月(日本標準時)
    day : int
        取得する日(日本標準時)
    mesh : str 
        図示する地域のメッシュコード
    """
    begin_datetime = datetime(year, month, day, 0, 0, 0, 0)
    end_datetime = begin_datetime + timedelta(days=1)
   
    temp_start = begin_datetime

    periods = []
    while temp_start < end_datetime:
        periods.append(temp_start)
        temp_start = temp_start + timedelta(minutes=INTERVAL)

    counts = []
    for i in range(len(periods)):
        begin_time = (periods[i] - timedelta(hours=9)).strftime(TIME_FORMAT)
        found = next((d['count'] for d in data if d['begin_time']==begin_time and d['mesh']==mesh) ,0)
        counts.append(found)

    fig, ax= plt.subplots(figsize=(12, 6))
    ax.plot(periods, counts, label = 'no marker')
    xfmt = mdates.DateFormatter("%H")
    xloc = mdates.HourLocator()
    ax.xaxis.set_major_locator(xloc)
    ax.xaxis.set_major_formatter(xfmt)
    ax.set_xlabel("hour")
    ax.set_ylabel("count")
    ax.set_xlim(periods[0], periods[-1]) 
    plt.show()
    
plot_daily_count(data_ginza_20170401, 2017, 4, 1, '5339460114')
2017年4月1日の銀座駅周辺の滞在人数(横軸が時間、縦軸が人数)(出典:ブログウォッチャー)

続いて、2017年4月1日の築地市場周辺(メッシュコード:5339369123)のデータを1時間毎に取得しグラフ化します。

tsukiji_bbox = [139.768487,35.660063, 139.772149,35.662768]
data_tsukiji_20170401 = get_profile_passport_count_per_hour(2017, 4, 1, 0, tsukiji_bbox, days=1)

print(data_tsukiji_20170401[:3])
plot_daily_count(data_tsukiji_20170401, 2017, 4, 1, '5339369123')
2017年4月1日の築地市場周辺の滞在人数(横軸が時間、縦軸が人数)(出典:ブログウォッチャー)

2つのグラフを単純に見比べるだけでも、一般的な商業施設が集まる銀座は昼から夕方にかけて人が集まるのに対し、市場である築地は朝から昼にかけて人が集まっており、地域性によりピーク時間が異なることがわかります。

3. 1ヶ月の人の動きをグラフ化しよう

次に、1ヶ月間で滞在人数がどれだけ変化するか調べてみましょう。

以下のサンプルコードを実行してください。
サンプルでは2017年4月の神宮球場周辺(メッシュコード:5339450734)のデータを1時間毎に取得しています。
※実行して結果が返ってくるまで、数分以上かかる場合があります。

jingu_bbox = [139.715106, 35.672617, 139.719169, 35.675419]

start_day = 1
duration = 10
end_day = 30
data_jingu_201704 = []
while start_day <= end_day:
    data_jingu_201704.extend(get_profile_passport_count_per_hour(2017, 4, start_day, 0, jingu_bbox, days=duration))
    start_day += duration
        
def plot_monthly_count(data, year, month, mesh):
    """
    1ヶ月のprofile_passportの結果をグラフ化

    Parameters
    ----------
    year : int
        取得する年(日本標準時)
    month : int
        取得する月(日本標準時)
    mesh : str 
        図示する地域のメッシュコード
    """
    begin_datetime = datetime(year, month, 1, 0, 0, 0, 0)
    end_datetime = begin_datetime + relativedelta.relativedelta(months=1,day=1)
   
    temp_start = begin_datetime

    periods = []
    while temp_start < end_datetime:
        periods.append(temp_start)
        temp_start = temp_start + timedelta(minutes=INTERVAL)

    counts = []
    for i in range(len(periods)):
        begin_time = (periods[i] - timedelta(hours=9)).strftime(TIME_FORMAT)
        found = next((d["count"] for d in data if d['begin_time']==begin_time and d['mesh']==mesh) ,0)
        counts.append(found)
    
    fig, ax= plt.subplots(figsize=(12, 6))
    ax.plot(periods, counts, label = 'no marker')
    xfmt = mdates.DateFormatter('%m/%d')
    ax.set_xticklabels(periods, size='small')
    ax.xaxis.set_major_formatter(xfmt)
    ax.set_xlabel("day")
    ax.set_ylabel("count")
    ax.set_xlim(periods[0], periods[-1]) 
    plt.show()
    
plot_monthly_count(data_jingu_201704, 2017, 4, '5339450734')
2017年4月の神宮球場周辺の滞在人数(横軸が日時、縦軸が人数)(出典:ブログウォッチャー)

人数が多い日が飛び飛びで存在していますが、調べてみると東京ヤクルトスワローズのホーム戦が4/1 – 4/2、4/12 – 4/13、4/21 – 4/23、4/28 – 4/30と行われており、グラフの尖った部分の日付と一致しています(他にも東京六大学の試合や東京都高校野球の試合が開催された日に小山が立っています)。
東京ヤクルトスワローズ 試合日程・結果 2017年4月
データをうまく加工することで、以下のように3次元プロットも可能です。ぜひ挑戦してみてください。

(出典:ブログウォッチャー)

2017年4月1日から7日にかけての東京ディズニーランド、ディズニーシー滞在人数
(x, y)=(3, 2)近辺がランドで、(x, y)=(6, 3)近辺がシーです。

以上が、TellusのJupyter Labを使って位置情報データ「Profile Passport」を取得する方法でした。

滞在人数は、他のデータと組み合わせたり、衛星画像を解析する際の補正情報として利用できます。また人の動きをグラフとして眺めるだけでも得られる気づきはたくさんあります。

東京以外の地域のデータも今後公開していく予定ですので、楽しみにお待ちください。