Profile PassportのAPIを利用する

はじめに

本記事ではTellusのJupyterLabを使って位置情報データ「Profile Passport」を取得する方法を紹介します。

Profile PassprotのAPIに必要なパラメータは期間(begin_timeとend_time)、時間頻度(interval)、そして地理的な範囲(bounds)となります。詳しくは商品情報のAPIリファレンスを参照してください。

https://pflow.tellusxdp.com/api/v1/profile_passport/get_count{?begin_time,end_time,interval,bouds}
Name Input Description
begin_timestring(query) 必須 開始時刻 UTC (yyyy-mm-dd HH:MM:SS) ※端数の時刻は、時間頻度(分)単位で切り上げされます。例:’2017-01-01 09:00:00’
end_timestring(query) 必須 終了時刻 UTC (yyyy-mm-dd HH:MM:SS) ※端数の時刻は、時間頻度(分)単位で切り捨てされます。例:’2017-01-01 10:59:59’
intervalnumber(query) 必須 時間頻度 ※開始時刻~終了時刻の間で集計する間隔を30(分)か、または60(分)で指定します。例:60(30 or 60)
boundsnumber(query) 必須 緯度・経度での地理的範囲 ※カンマ区切りで最小緯度,最小経度,最大緯度,最大経度の順で設定します。例:’-90.0,-180.0,90.0,180.0’

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

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

「Profile Passport」とは、プロファイルパスポート SDKを通して、スマートフォンアプリから取得された位置情報データのことです。東京近郊に限られますが、2016年7月から2017年9月までの期間の「Profile Passport」のデータを、Tellus上で利用できます。

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

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

devprofilepass_api1_20200220_7.png

人の多さをヒートマップで可視化しています
出典:ブログウォッチャー

Profile Passportのデータを取得する関数を定義しましょう。関数ではデータの開始時間や終了時間などを変数として入力する必要があります。

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) 

トークンはマイページのAPIアクセス設定(要ログイン)から取得してください。取得できる位置情報データは以下の通りです。

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

日本時間が分かりやすいためデータを取得する関数の開始時間は日本時間で入力する形としていますが、APIは世界標準時を基準に構成されています。そのため次の関数内では日本時間を世界標準時に直す処理を行なっています。さらに、データを取得する領域を指定し、その領域に対して一定間隔のデータを取得するための処理を加えていきます。

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

定義した関数を実行しましょう。取得するデータは4月1日の12時から13時までの新宿西口付近です。

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)

devprofilepass_api1_20200220_2.png

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の間の値です。meshは地域メッシュコードを表します。地域メッシュコードとは、日本工業規格で定められた区域を識別するコードのことで、このAPIでは4分の1地域メッシュ区分で値が返却されます。

Tellus OSではOS右上にあるツールからグリッドツールを選択し、メッシュコードの表示をオンにすることで地域メッシュを確認できます。地域メッシュについて詳しくはこちらを参考にしてください。

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])

devprofilepass_api1_20200220_1.png

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

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

# load libraries
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
%matplotlib inline
# only begin_data is enough to draw the transitional data
x_period = []
y_value = []
for i in range(len(data_ginza_20170401)):
    x_period.append(datetime.strptime(data_ginza_20170401[i]['begin_time'],"%Y-%m-%d %H:%M:%S") + timedelta(hours=9))
    y_value.append(data_ginza_20170401[i]['count'])
# change date format
x_period = [x.strftime("%m/%d %H:%M:%S") for x in x_period]
fig, ax= plt.subplots(figsize=(12, 6))
ax.plot(x_period, y_value, label = 'no marker')
plt.xticks(rotation=90)
ax.set_xlabel("time")
ax.set_ylabel("count")
ax.set_xlim(x_period[0], x_period[-1])
plt.show()

devprofilepass_api1_20200220_3.png

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

時間は日本時に戻してあります。次に上の処理を関数化します。

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')

devprofilepass_api1_20200220_4.png

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')

devprofilepass_api1_20200220_8.png

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

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

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
len(data_jingu_201704)

700件のデータを取得することができました。続いて、グラフの描画を行います。手順は1日の動きをグラフにしたのとほぼ同じになります。

# only begin_data is enough to draw the transitional data
x_period = []
y_value = []
for i in range(len(data_jingu_201704)):
    x_period.append(datetime.strptime(data_jingu_201704[i]['begin_time'],"%Y-%m-%d %H:%M:%S")+ timedelta(hours=9))
    y_value.append(data_jingu_201704[i]['count'])
# Axes settings are in Locator and Formatter
fig, ax= plt.subplots(figsize=(12, 6))
ax.plot(x_period, y_value, label = 'no marker')
# set ticks
ax.xaxis.set_major_locator(mdates.DayLocator(bymonthday=None, interval=4, tz=None))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d"))
# rotate ticks and set font size
labels = ax.get_xticklabels()
plt.setp(labels, rotation=45, fontsize=10);
# show line graph with grids
ax.grid()

devprofilepass_api1_20200220_5.png

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

続いて、関数として定義します。

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')

devprofilepass_api1_20200220_9.png

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

2017年4月の神宮球場周辺の滞在人数(横軸が日時、縦軸が人数)(出典:ブログウォッチャー)

人数が多い日が飛び飛びで存在していますが、調べてみると東京ヤクルトスワローズのホーム戦が4/1 – 4/2、4/12 – 4/13、4/21 – 4/23、4/28 – 4/30と行われており、グラフの尖った部分の日付と一致しています(他にも東京六大学の試合や東京都高校野球の試合が開催された日に小山が立っています)。

東京ヤクルトスワローズ 試合日程・結果 2017年4月

データをうまく加工することで、以下のように3次元プロットも可能です。ぜひ挑戦してみてください。

passportprofile3d_20200221.gif

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

2017年4月1日から7日にかけての東京ディズニーランド、ディズニーシー滞在人数

(x, y)=(3, 2)近辺がランドで、(x, y)=(6, 3)近辺がシーです。

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

指定した空間での滞在人数は、POS(Point of Sales)情報を始めとした他のデータと組み合わせることで、付加価値が増します。気象情報を含めた衛星データなどを組み合わせることで、人流データに付加価値をつけることも可能です。人の動きをグラフとして眺めるだけでも得られる気づきはたくさんあります。東京以外の地域のデータも今後公開していく予定ですので、楽しみにお待ちください。

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)
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])
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
get_ipython().run_line_magic('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')
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')
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')