アナリスト、データサイエンティスト、開発者向けのガイド
概要 Mapbox Movement は、世界で最も包括的なプライバシーを重視した位置情報データセットです。都市または特定の地域を人々がどのように、どこで、いつ移動するかについて、高解像度のビューを提供します。過去数か月間、Mapbox Movementデータを使用して、4月上旬のパンデミックによる閉鎖のピーク時 、2021年の米国大統領就任式 、および小売商業の再開と回復プロセス 中の活動を調査しました。
昨年末、米国、ドイツ、英国におけるMapbox Movementデータの1年分をリリースし、すべてのMapbox開発者がこちら から無料で利用できるようになりました。このブログ記事では、このデータの実際の分析例を紹介します。このデータへのアクセス方法、視覚化方法(コーディング経験は不要です!)、およびさまざまなビジネス拠点(この例では主要空港)でのアクティビティの分析方法について説明します。
グラフ1:サンフランシスコ国際空港、デンバー国際空港、ダラス/フォートワース国際空港、およびダレス国際空港での正規化された集計アクティビティ。すべてのシリーズは、2020年1月4日から1月31日までの集計アクティビティに正規化されています。ソース:Mapbox Movementデータ。 to このチュートリアルの終わりには、これらの方法を適用して、小売業の回復分析で行ったものと同様に、さまざまな業界や問題領域を分析して、独自の興味深い洞察を得ることができるようになります。地図の視覚化を含むこのチュートリアルのすべての手順は、単一のPython Jupyterノートブックで実行できます。公開されているノートブックとコードの詳細については、GitHub およびAWS S3 で確認できます。
これから行うこと まず、Mapbox Movementのサンプルデータを簡単に見て、コードをほとんど記述せずにこのデータにアクセスして視覚化する方法を理解します。次に、OurAirportsの最新の空港データセット の座標といくつかの属性を使用して、分析を実行する興味深い空港を見つけます。これらの座標に基づいて、いくつかの地理空間ツールを使用して、各空港のおおよその境界線を描画します。次に、2020年を通して、これらの空港境界内における空間的および時間的な移動傾向を分析および比較します。
MAPBOX ACTIVITY INDEXとは? Mapbox Movementは、空間と時間の両方における相対的な人間の活動(歩行、運転など)の指標を提供し、アクティビティインデックスとして提供されます。アクティビティインデックスは、指定された期間および地理的地域における活動レベルを反映するMapbox独自の指標です。このインデックスは、モバイルデバイスからの匿名化された位置データを、1日または1か月ごと、および約100メートルの解像度の地理タイルごとに集計することによって計算されます。これらの地理タイルは、指定されたOpenStreetMapズームレベル での地理的区分への数値参照であるquadkeyに基づいています。1日およびタイルごとのアクティビティは、ベースラインを基準として正規化されます。ベースラインは、各国の2020年1月の99.9パーセンタイルの日/タイルにおけるアクティビティを表します。言い換えれば、米国の2020年1月に最もアクティビティが高かった都市ブロックのアクティビティインデックス値は約1になり、3月のある日にそのアクティビティの半分しかなかった都市ブロックのアクティビティインデックス値は0.5になります。アクティビティインデックスの計算方法の詳細については、Movementドキュメント を参照してください。
コードなしでアクティビティデータを視覚化する迅速かつ簡単な方法! 画像は千の言葉に値します。特に、結果をチームに提示する必要がある場合はそうです。データを迅速に視覚化し、Mapbox Movementデータセットを理解する方法を知ることが重要です。
2020年1月1日のサンフランシスコ湾岸地域の移動データで構成されるサンプルCSVファイルにアクセスするには、このリンク をクリックしてください。
サンプルCSVファイルは、サンフランシスコベイエリアを対象とした2020年1月1日の移動データで構成されています。 このCSVファイルは、quadkey別に、日ごとに集計されたアクティビティ指標の例です。
bounds フィールドは、各クアッドキー(おおよそ都市の1区画のサイズ)の外側の角の座標を提供し、xlat およびxlon フィールドは、各クアッドキーの重心を示します。agg_day_period フィールドには、この例では2020-01-01と表示されるはずです。最も重要なフィールドはactivity_index_total で、各街区の正規化されたアクティビティを示します。 このCSVファイルの各行項目は、すでに緯度と経度の座標に変換されています。このCSVファイルをKepler.gl にドラッグアンドドロップするだけで、アクティビティインデックスを可視化できます。 この地図上の各ポイントはクアッドキーを表しています。各ポイントにカーソルを合わせると、アクティビティレベル、重心、日付を確認できます。また、地図を拡大・縮小することで、ミクロなアクティビティパターンとマクロなアクティビティパターンを観察できます。
米国、ドイツ、英国におけるMapbox Movementのサンプルデータを1年分すべて入手するには、サンプルリクエスト を送信し、指示に従ってデータをダウンロードしてください。チュートリアルに進み、Movementデータのダウンロード方法、特定の地理的な場所でのアクティビティの分析方法を学んでください。
セットアップ Mapbox Movementのサンプルデータセットへのアクセス(Movementサンプルデータセットへのリクエスト アクセスは無料です)。 OurAirportsの最新の空港データセット から、airports.csv という名前のファイルをダウンロードします。このデータセットから、次の属性を使用します:type 、name 、latitude_deg 、longitude_deg 、iso_country 、iso_region 、municipality 、iata_code. 仮想環境にインストールされている次のPython依存関係のリストがあります。私の仮想環境とJupyterノートブックはPython 3.7.2で実行されています。 (オプション)GitHub またはAWS S3 からJupyter Notebookをダウンロードして、一緒に進めてください。ノートブックサーバーを起動する前に、有効なAWS認証情報が設定されている必要があります。これらのAWS認証情報は、Jupyter Notebookを実行しているターミナルウィンドウと同じターミナルウィンドウ に設定する必要があります。 Jupyter Notebookで移動データを可視化 Mapbox MovementデータをWebベース版のKepler.gl で視覚化するだけでなく、1月1日のサンフランシスコ・ベイエリアのMovementデータで構成されるサンプルCSVファイル をJupyter notebookにロードすることもできます。仮想環境にJupyter版のkepler.gl がインストールされていることを確認してください。次の関数download_csv_to_df.() を使用して、サンプルCSVファイルをJupyter notebookと同じディレクトリにダウンロードできます。
import os import requests from io import StringIO import pandas as pd def download_csv_to_df(url, parse_dates=False, sep=","): """ URLが与えられた場合、データをダウンロードしてPandas DataFrameに読み込みます。 Args: url (string): プロトコル(http)、ホスト名(www.example.com)、 およびファイル名(index. html)が含まれます。 parse_dates (boolean or list) : booleanまたは列名のリスト(str)、デフォルトはFalseです。 Raises: requests.exceptions.RequestException: リクエストの処理中に発生した例外。 Returns: Pandas DataFrame。 """ try: res = requests.get(url) except requests.exceptions.RequestException as e: raise SystemExit(e) return pd.read_csv(StringIO(res.text), parse_dates=parse_dates, sep=sep)
以下のコマンドをJupyter Notebookのセルに貼り付け、実行すると、以下のようなkepler.glマップにMovementデータが読み込まれるのを確認できます。このチュートリアルの後半では、各空港の境界線を描画して視覚化する際に、これが便利なツールとなることがわかります。
従来のJupyter NotebookではなくJupyterLabを使用している場合、kepler.glウィジェットの読み込みで問題が発生することがあります。この問題を回避するには、.save_to_html()メソッドを使用して、マップをインタラクティブなHTMLファイルとして保存します。その後、お好みのWebブラウザでこのHTMLファイルを操作できます。
from keplergl import KeplerGl # Download the sample CSV file consisting of January 1st, 2020 Movement data covering the San Francisco Bay Area. movement_url = 'https://mapbox-movement-public-sample-dataset.s3.amazonaws.com/v0.2/daily-24h/v2.0/US/quadkey/total/2020/01/01/data/0230102.csv' movement_df = download_csv_to_df(movement_url, sep="|") # Rename a few columns for clarity: movement_df = movement_df.rename( columns={"xlon": "lon", "xlat": "lat"} )[["lon", "lat", "activity_index_total"]] # Load an empty keplergl map sample_z7_map = KeplerGl() # Add movement sample csv. Kepler accepts CSV, GeoJSON or DataFrame! sample_z7_map.add_data(data=movement_df, name='data_2') # Optional: Save map to an html file sample_z7_map.save_to_html(file_name='sample_z7_map.html') # Load kepler.gl widget below a cell sample_z7_map
2020年1月1日のサンフランシスコ湾の活動を可視化するために、Jupyter notebookセルにロードされたKepler.glマップ。 空港活動分析 まず、調査対象の空港の地理的位置を特定することから始めましょう。最初に、以前に定義した関数download_csv_to_df() を使用して、airports.csv ファイルを最新のデータセット からOurAirports 経由でダウンロードし、データをPandas DataFrameに変換します:
# 空港データセットをダウンロードします airport_url = "https://ourairports.com/data/airports.csv" airports_df = download_csv_to_df(airport_url)
次に、不要な列をすべて削除し、主要な米国の空港のみを含むようにDataFrameをフィルタリングします。
# 不要な列を削除します airports_df = airports_df.drop(columns=['id', 'ident', 'elevation_ft','continent', 'scheduled_service', 'gps_code', 'local_code', 'home_link', 'wikipedia_link', 'keywords']) # 米国のみの空港にフィルタリングします us_airports_df = airports_df[airports_df["iso_country"] == 'US'] # データフレームをフィルタリングして、大規模な空港のみを含めます us_airports_df_large = us_airports_df[us_airports_df["type"] == "large_airport" ] us_airports_df_large = us_airports_df_large.sort_values(by=["iso_region"]]) # DataFrameの最初の数行を調べます us_airports_df_large.head()
このDataFrameの最初の5行を以下に示します。
us_airports_df_large DataFrameの最初の5行 各空港に関連するアクティビティを集計および分析するには、各空港の境界を定義する必要があります。この空港データセットには、米国に拠点を置く約25,000の空港があります。そのうち170は「大規模空港」として分類されています。各空港の境界を手動で描画することもできますが、時間がかかります。より効率的な方法は、Shapely やMercantile のような地理空間ライブラリを利用して、大規模な近似境界を描画することです。
まず、各空港の緯度と経度のペアをShapely Pointオブジェクト に変換します。これは、空間内の単一点を表します。
from shapely.geometry import Point def coords2points(df_row): """ "longitude_deg"と"latitude_deg"をShapely Pointオブジェクトに変換します。 Args: df_row (Pandas DataFrame row): 元の空港DataFrameからの特定の空港(行)またはpandas.core.series.Series。 Returns: shapely.geometry.pointオブジェクト。 """ return Point(df_row['longitude_deg'], df_row['latitude_deg']) # coords2points関数を各行に適用し、新しい列に結果を格納します us_airports_df_large['airport_center'] = us_airports_df_large.apply(coords2points, axis=1)
次に、Shapelyの buffer() メソッドを介して、各空港のShapely Pointオブジェクトの周りに半径1,000メートルの円を作成します。この円は、すべての空港の「おおよその境界」として機能します。これは、Shapely Polygonオブジェクトとして保存されます。地球の表面で円を計算するため、このプロセスには少し投影の工夫が必要です。AEQD投影とWGS84の間で座標を変換する必要がある理由の詳細については、付録のセクションIを参照してください。
from functools import partial import pyproj from shapely import geometry from shapely.geometry import Point from shapely.ops import transform def aeqd_reproj_buffer(center, radius=1000): """ Converts center coordinates to AEQD projection, draws a circle of given radius around the center coordinates, converts both polygons back to WGS84. Args: center (shapely.geometry Point): center coordinates of a circle. radius (integer): circle's radius in meters. Returns: A shapely.geometry Polygon object for cirlce of given radius. """ # Get the latitude, longitude of the center coordinates lat = center.y long = center.x # Define the projections local_azimuthal_projection = "+proj=aeqd +R=6371000 +units=m +lat_0={} +lon_0={}".format( lat, long ) wgs84_to_aeqd = partial( pyproj.transform, pyproj.Proj("+proj=longlat +datum=WGS84 +no_defs"), pyproj.Proj(local_azimuthal_projection), ) aeqd_to_wgs84 = partial( pyproj.transform, pyproj.Proj(local_azimuthal_projection), pyproj.Proj("+proj=longlat +datum=WGS84 +no_defs"), ) # Transform the center coordinates from WGS84 to AEQD point_transformed = transform(wgs84_to_aeqd, center) buffer = point_transformed.buffer(radius) # Get the polygon with lat lon coordinates circle_poly = transform(aeqd_to_wgs84, buffer) return circle_poly
us_airports_df_large DataFrame内のすべての行に上記のaeqd_reproj_buffer.() 関数を適用して、すべての空港に対してこのステップを繰り返します。
# aeqd_reproj_buffer関数をすべての空港座標にマッピングします。 us_airports_df_large["aeqd_reproj_circle"] = us_airports_df_large["airport_center"].apply(aeqd_reproj_buffer)
次に、kepler.glを使用して、任意の空港の中心座標と1 kmの円を視覚的に検査できます。
from shapely.geometry import mapping from keplergl import KeplerGl def plot_circles_kepler(airport, to_html=False): """ Plots the center and 1 km circle polygons using kepler. Args: airport (pandas.core.series.Series): an specific airport (row) from the main airports DataFrame. to_html (boolean): if True, saves map to an html file named {airport_iata_code}_circle.html. Returns: a kepler interactive map object. """ # Get the center (Shapely Point), circle (Shapely Polygon), and iata code of the airport circle_center = airport["airport_center"] circle_poly = airport["aeqd_reproj_circle"] iata = airport["iata_code"] # Define the GeoJSON object for the 1 km circle circle_feature = { "type": "Feature", "properties": {"name": "Circle"}, "geometry": mapping(circle_poly) } # Define the GeoJSON object for the center of the circle center_feature = { "type": "Feature", "properties": {"name": "Center"}, "geometry": mapping(circle_center) } # Define the Kepler map configurations config = { 'version': 'v1', 'config': { 'mapState': { 'latitude': airport["latitude_deg"], 'longitude': airport["longitude_deg"], 'zoom': 12 } } } # Load the keplergl map of the circle and add center circle_map = KeplerGl(data={'Circle': circle_feature}) circle_map.add_data(data=center_feature, name='Center') circle_map.config = config # Optional: Save map to an html file if to_html: circle_map.save_to_html(file_name=f'{iata}_circle.html') return circle_map
たとえば、次のコードスニペットを実行して、サンフランシスコ国際空港(SFO)周辺のエリアを視覚化します。
# サンフランシスコ国際空港周辺に1 kmの円のポリゴンをプロットします sfo = us_airports_df_large[us_airports_df_large["iata_code"] == "SFO"].iloc[0] plot_circles_kepler(sfo)
kepler.glは、一連のMapboxベースマップスタイルを提供し、背景地図を簡単に変更できます。ベースマップパネル を開き、デフォルトのマップスタイルリストからさまざまな背景地図スタイルを試してください。ここでは、Mapbox Satelliteを選択しました:
Mapbox Movement quadkeyデータは、都市ブロックのサイズ(約100メートルの解像度)であるズーム18 で集計されます。What the Tileインタラクティブツール を使用して、さまざまなサイズのquadkeyタイルを視覚化できます。たとえば、サンフランシスコ国際空港(SFO)で描いた1 kmの円と重なるズーム18 quadkeyの一部を次に示します。
サンフランシスコ国際空港(SFO)の1 kmの円と重なるズーム18のクアッドキーの例。 ズーム18のアクティビティデータはCSVファイルに保存されており、各ファイルには単一のズーム7タイルに関するデータが含まれています。What The Tile を使用して、SFO空港を含むエリアのズーム7クアッドキー番号を調べることができます。この場合、ズーム7番号は0230102です(ちなみに、ズーム7番号は、空港をカバーするズーム18番号の最初の7桁にすぎません)。ズーム7タイルは、サンフランシスコ・ベイエリアのより広い地域をカバーしています。
サンフランシスコ国際空港(SFO)で描いた1 kmの円と重なるズーム7のquadkey SFOおよびベイエリアの残りの部分を含むズーム7タイルに関する1月1日の日次アクティビティデータは、AWS S3の次のパスに保存されます:s3://mapbox-movement-public-sample-dataset/v0.2/daily-24h/v2.0/US/quadkey/total/2020/01/01/data/0230102.csv 。
より効率的な方法として、以下のgenerate_quadkeys.() メソッドを使用して、特定のズームレベルにおいて各空港から半径1km以内にあるすべてのquadkeyタイルを検索します。SFOに対して作成した円形のShapely Polygonオブジェクトとズームレベル7を入力すると、この関数はズーム7のquadkey 0230102を返します。SFOに対して作成した円形のShapely Polygonオブジェクトとズームレベル18を入力すると、What The Tile で以前に見たズーム18のquadkeyの完全なリストが返されます。一部の空港のポリゴンは、複数のz7 quadkeyにまたがることに注意してください。
import mercantile def generate_quadkeys(circle_poly, zoom): """ 空港と重なるクアッドキーのリストを生成します。 Args: circle_poly (shapely.geometry Polygon): 空港の周りに描かれた円のポリゴンオブジェクト。 zoom (integer): ズームレベル。 Return: 文字列としてのクアッドキーのリスト """ return [mercantile.quadkey(x) for x in mercantile.tiles(*circle_poly.bounds, zoom)]
この方法を使用して、すべての空港のズーム18およびズーム7クアッドキーをすばやく生成できます。各空港の1 kmの円ポリゴンを生成し、それらをaeqd_reproj_circle 列のus_airports_df_large DataFrameに格納したことを思い出してください。
# 各空港の重複するz18クアッドキーのリストを作成し、新しい列に追加します us_airports_df_large['z18_quadkeys'] = us_airports_df_large.apply(lambda x: generate_quadkeys(x['aeqd_reproj_circle'], 18),axis=1) # 各空港の重複するz7クアッドキーのリストを作成し、新しい列に追加します us_airports_df_large['z7_quadkeys'] = us_airports_df_large.apply(lambda x: generate_quadkeys(x['aeqd_reproj_circle'], 7),axis=1)
これらのズーム18とズーム7のQuadKeyを取得したら、Mapbox Movementのサンプルデータセットからアクティビティデータをダウンロードする準備が整いました。以下のdownload_sample_data_to_df() 関数を使用すると、生のアクティビティデータをダウンロードし、指定された開始日と終了日の間のすべての日付にわたるPandas DataFrameを作成できます。
import os import requests from datetime import datetime, timedelta def download_sample_data_to_df(start_date, end_date, z7_quadkey_list, local_dir, verbose=True): """ Downloads Movement z7 quadkey CSV files to a local dir and reads Read the CSV file into a Pandas DataFrame. This DataFrame contains **ALL** Z18 quadkey data in this Z7 quadkey. Args: start_date (string): start date as YYYY-MM-DD string. end_date (string): end date as YYYY-MM-DD string. z7_quadkey_list (list): list of zoom 7 quadkeys as string. local_dir (string): local directory to store downloaded sample data. verbose (boolean): print download status. Raises: requests.exceptions.RequestException: An exception raised while handling your request. Returns: a Pandas DataFrame consists of Z7 quadkey data. DataFrame contains **ALL** Z18 quadkey data in this Z7 quadkey. """ bucket = "mapbox-movement-public-sample-dataset" # Generate range of dates between start and end date in %Y-%m-%d string format start = datetime.strptime(start_date, '%Y-%m-%d') end = datetime.strptime(end_date, '%Y-%m-%d') num_days = int((end - start).days) days_range = num_days + 1 date_range = [(start + timedelta(n)).strftime('%Y-%m-%d') for n in range(days_range)] sample_data = [] for z7_quadkey in z7_quadkey_list: for i in range(len(date_range)): yr, month, date = date_range[i].split('-') url = f"https://{bucket}.s3.amazonaws.com/v0.2/daily-24h/v2.0/US/quadkey/total/2020/{month}/{date}/data/{z7_quadkey}.csv" if not os.path.isdir(os.path.join(local_dir, month, date)): os.makedirs(os.path.join(local_dir, month, date)) local_path = os.path.join(local_dir, month, date, f'{z7_quadkey}.csv') if verbose: print (z7_quadkey, month, date) print (f'local_path : {local_path}') try: res = requests.get(url) df = pd.read_csv(StringIO(res.text), sep='|') convert_dict = {'agg_day_period': 'datetime64[ns]', 'activity_index_total': float, 'geography': str} df = df.astype(convert_dict) # Keep leading zeros and save as string df['z18_quadkey'] = df.apply(lambda x: x['geography'].zfill(18), axis=1).astype('str') df['z7_quadkey'] = df.apply(lambda x: x['geography'][:6].zfill(7), axis=1).astype('str') sample_data.append(df) df.to_csv(local_path, index=False) except requests.exceptions.RequestException as e: raise SystemExit(e) if verbose: print (f'Download completed for {z7_quadkey} over date range {start_dt} to {end_dt}') return pd.concat(sample_data)
単一の空港からの複数のズーム7クアッドキー、または複数の空港からのズーム7クアッドキーを渡すことができます。たとえば、サンフランシスコ国際空港(SFO)とデンバー国際空港(DEN)の場合、2020年1月1日から2020年1月2日までのすべてのアクティビティデータをダウンロードできます。
# Tweak the following set of parameters to include broader time frame or more airports start_dt_str = "2020-01-01" end_dt_str = "2020-01-02" # Find SFO and DEN from the us_airports_df_large DataFrame sfo = us_airports_df_large[us_airports_df_large["iata_code"] == "SFO"].iloc[0] den = us_airports_df_large[us_airports_df_large['name'].str.contains("Denver")].iloc[0] # Add these two airports airports = [sfo, den] # Creates a list of z7 quadkeys to download z7_quadkeys_to_download = [] for airport in airports: for z7_quadkey in airport["z7_quadkeys"]: z7_quadkeys_to_download.append(z7_quadkey) # Define a list to append all newly created DataFrames sample_data_airports = [] for z7 in z7_quadkeys_to_download: local_directory = os.path.join(os.getcwd(), f'sample_data_{z7}') print ([z7]) print (local_directory) # Run the download script sample_data_airports.append(download_sample_data_to_df(start_dt_str, end_dt_str, [z7], local_directory, False)) # Create a DataFrame of all z7 quadkey activity data sample_data_airports_df = pd.concat(sample_data_airports).sort_values(by=['agg_day_period', 'z18_quadkey'])
このMapbox Movementデータセットは、日付がagg_day_period という列で指定され、日ごとに集計されています。zoom18 quadkey(z18_quadkey という列)ごとに、日付ごとに単一のアクティビティ指標値(activity_index_total という列)があります。まず、SFOの1 kmの円ポリゴンを含むすべての地域のデータのみが含まれるように、DataFrameをフィルタリングします。
# DataFrameをフィルタリングして、SFO z7クアッドキーからのアクティビティデータのみを含めます。DEN z7クアッドキーデータを除外します。 sample_data_sfo = [] for z7_quadkey in sfo["z7_quadkeys"]: sample_data_sfo.append(sample_data_airports_df[sample_data_airports_df["z7_quadkey"] == z7_quadkey]) # すべてのSFOアクティビティデータのDataFrameを作成します sample_data_df_sfo = pd.concat(sample_data_sfo)
この初期DataFrameには、SFOに必要なズーム18のquadkeyデータが大量に含まれていることに注意してください。これらのズーム18のquadkeyがすべて、SFOを中心とする1 kmの円と重なっているわけではありません。したがって、このDataFrameをフィルタリングして、次のステップで不要なズーム18のquadkeyエントリを削除することが重要になります。
# DataFrameをフィルタリングして、1 kmの円と重なるZ18クアッドキーのエントリのみを含めます z18_sample_data_sfo_df = sample_data_df_sfo[sample_data_df_sfo["z18_quadkey"].isin([z18_qk for z18_qk in sfo["z18_quadkeys"]])]
2020年1月の2日間で、SFOの1 kmの円と重なる関連するズーム18クアッドキーのエントリのみを含む DataFrame ができました。
z18_sample_data_sfo_df DataFrame(2020年1月1日~2020年1月2日)の出力 先ほどの2日間の例と比較すると、1年分のデータをダウンロードしてフィルタリングするには、より長い時間がかかります。時間を節約するために、ダラス/フォートワース国際空港(DFW)、デンバー国際空港(DEN)、ダレス国際空港(IAD)、サンフランシスコ国際空港(SFO)の4つの空港について、2020年1月1日から2020年12月31日までのアクティビティデータのダウンロード手順を事前に処理しました。このデータは圧縮され、AWS S3上の公開されているMapbox Movementサンプルデータバケットにzipファイルとしてアップロードされています。付録の第III部では、これらのzipファイルにアクセスする方法と、これらのより大きなズーム7クアッドキーDataFrameが詳細な分析にどのように役立つかを示します。
また、フィルタリング手順を事前に処理し、4つの空港それぞれの1 km圏と重複する関連するすべてのズーム18クアッドキーからのアクティビティデータを同じS3バケットにアップロードしました。以前に定義した関数download_csv_to_df() を使用して、このデータをダウンロードできます。引き続きSFOを例として使用してください。
# DataFrameでサンフランシスコ国際空港の行を見つけます sfo = us_airports_df_large[us_airports_df_large["iata_code"] == "SFO"].iloc[0] # SFOの事前処理されたCSVファイルのURLを定義します z18_sample_data_sfo_url = 'https://mapbox-movement-public-sample-dataset.s3.amazonaws.com/airports_dataset/data/z18_sample_data_sfo.csv' # sfoのフィルタリングされたサンプルデータをダウンロードし、DataFrameに読み込みます。agg_day_periodをdatetimeオブジェクトとして解析します z18_sample_data_sfo_df = download_csv_to_df(z18_sample_data_sfo_url, parse_dates=['agg_day_period'])
また、後で比較を行う際に空港を区別できるように、元の米国の大規模空港DataFrameからIATA空港コードを取得したいと考えています。最後に、DataFrameを日付でソートします。
# 1 km2の半径の円と重なるすべてのZ18クアッドキーの総数を検索します sfo_z18_sample_df['total_num_z18_qks'] = float(len(set(z18qks_sfo))) # IATA空港コードを取得します sfo_z18_sample_df['iata'] = sfo['iata_code'] # 日付でソートします sfo_z18_sample_df = sfo_z18_sample_df.sort_values(by=['agg_day_period', 'z18_quadkey'])
これは、結果として得られるDataFrameの外観です。
z18_sample_data_sfo_df DataFrame(2020年1月1日~2020年12月31日)の出力 残りの3つの空港(ダラス/フォートワース国際空港(DFW)、デンバー国際空港(DEN)、およびダレス国際空港(IAD))についても、この手順を繰り返すことができます。詳細なコードは、GitHub およびAWS S3 で入手できるJupyterノートブックにあります。
次に、各空港に関する興味深い統計を生成します。
I. ターゲット空港と重複するすべてのZ18 QUADKEYについて、日次アクティビティ指標を集計する 以下のコードスニペットは、SFOの1日あたりの集計アクティビティを計算します。
# Calculate the sum of activity per day and construct a DataFrame sum_data = sfo_z18_sample_df.groupby(['agg_day_period', 'total_num_z18_qks', 'iata']).sum()['activity_index_total'] # rename the column sfo_ai_stats_df = pd.DataFrame(sum_data).reset_index().rename(columns={'activity_index_total':'sum_ai_daily'}) # Set agg_day_period as the index sfo_ai_stats_df = sfo_ai_stats_df.set_index('agg_day_period')
他の3つの空港すべてについてこのステップを繰り返し、4つの空港すべての活動指数の1日の合計をプロットして比較します。以下のチャートを生成するスクリプトは、Jupyter notebook にあります。
グラフ2:サンフランシスコ国際空港、デンバー国際空港、ダラス/フォートワース国際空港、およびダレス国際空港における2020年1月1日から12月31日までの1日の総活動量。出典:Mapbox Movementデータ。 上のグラフから、DFWは一般的に4つの空港の中で最も高い活動レベルを持ち、DENが2番目に高いことがわかります。すべての空港での活動はパンデミックの開始後に減少しましたが、このパターンは一年を通して維持されています。
この分析には注意点があります。各空港の面積を、重心点から半径1 kmの円として推定したことを思い出してください。これは完全に正確ではない可能性があります。たとえば、空港によっては他の空港よりも大きい場合があり、使用した重心が一部の空港のターミナルに近い場合があります。空港間には、さらに微妙な違いがあるかもしれません。たとえば、ターミナルが手荷物受取所から遠いため、一部の空港では人の移動が多い場合があります。絶対的な活動レベルを適切に比較するには、各空港の面積をより正確に定義し(詳細については下記)、空港の活動の他の特徴も考慮に入れる必要があります。
II. 空港間のパンデミック後の活動の変化を比較する 絶対的なアクティビティの合計を比較する代わりに、空港間のアクティビティパターンの変化を比較する方が有益な場合があります。そのため、2020年1月の同じ月において、すべての空港のアクティビティを正規化できます(通常、元旦周辺では非典型的なアクティビティパターンが見られるため、1月1日から1月3日は除外します)。次に、異なる空港でのアクティビティの時系列が、この開始値からどのように乖離しているかを視覚化できます。
この分析は、たとえば、パンデミックの開始後、一部の空港で他の空港よりも活動が低下したかどうか、また、一部の空港でより回復したかどうかを理解するのに役立ちます。このタイプの分析は、上記の課題に対してもより耐性があります。空港自体が変化していないと仮定する限り、各空港の面積の正確な定義や、空港間の活動の変化の方向性を比較するために、空港の構造を理解する必要はありません。
from datetime import timedelta, date # 2020年1月4日から1月31日までのタイムスタンプを見つけ、日付のリストを生成します jan_start_date = datetime.strptime('2020-01-04', "%Y-%m-%d") jan_end_date = datetime.strptime('2020-01-31', "%Y-%m-%d") jan_dates = pd.date_range(jan_start_date, jan_end_date, freq='D') # SFOの2020年1月4日から1月31日までの集計されたアクティビティを計算します sfo_sum_ai_Jan_2020 = sfo_ai_stats_df[sfo_ai_stats_df.index.isin([x for x in jan_dates])].sum()['sum_ai_daily'] sfo_ai_stats_df["sum_ai_Jan_2020"] = sfo_sum_ai_Jan_2020 # 1月4日から1月31日までのアクティビティの合計で、毎日のアクティビティの合計を正規化し、DataFrameに追加します。 sfo_ai_stats_df["normalized_sum"] = 28 * sfo_ai_stats_df["sum_ai_daily"] / sfo_sum_ai_Jan_2020 # DataFrameを調べます sfo_ai_stats_df
以下の正規化された活動指数曲線を使用すると、2020年4月12日のパンデミックによる閉鎖のピーク時の活動指数を、4つの空港すべてで夏と冬の月と比較できます。
グラフ3:2020年4月12日の正規化された1日の総活動量と、サンフランシスコ国際空港、デンバー国際空港、ダラス/フォートワース国際空港、およびダレス国際空港における感謝祭を比較します。2020年12月31日まで。すべての系列は、2020年1月4日から1月31日までの総活動量に正規化されています。出典:Mapbox Movementデータ。 一般的に、4つの空港すべてが年間を通して同様の活動傾向を示していることがわかります。上記のグラフは、3月中旬から4月にかけて、渡航制限により航空旅行が急激に減少したことを示しています。
以上です!これで、スーパーマーケットの代わりに空港を使って、小売業の回復分析 を再現できました。この同じ方法論は、さまざまな業界や問題領域の分析に使用できます。日々の変動が大きいため、上記のグラフがノイズが多いように見える場合は、移動平均を計算してグラフを平滑化し、マクロトレンドをより簡単に視覚化することを検討するとよいでしょう。これにより、次の統計を計算できます。
III. ターゲット空港と重複するすべてのZ18 QUADKEYの7日間の移動平均を計算する これにより、各空港のアクティビティ指標の日次集計で観察されるスパイクと変動が均一化された、滑らかな曲線が生成されます。
# アクティビティ指数の7日間のローリング平均を計算します。 sfo_ai_stats_df["sum_ai_daily_rolling_avg"] = sfo_ai_stats_df["sum_ai_daily"].rolling(window=7).mean()
4つの空港すべての7日間のローリング平均アクティビティ指標をプロットします:
グラフ4:サンフランシスコ国際空港、デンバー国際空港、ダラス/フォートワース国際空港、およびダレス国際空港における2020年1月1日から12月31日までの1日の総活動量の7日間移動平均。出典:Mapbox Movementデータ。 興味深いことに、予想通り、感謝祭のアクティビティの低下は移動平均に吸収されるため、ユースケースと提示する洞察に応じて、適切な視覚化を選択することが重要です。
上記のグラフ2から、活動指標の正規化された日次集計値の7日間の移動平均を作成して、スパイクと変動を平滑化することもできます。
# 正規化された日次集計アクティビティの7日間のローリング平均を計算します sfo_ai_stats_df["norm_sum_rolling_avg"] = sfo_ai_stats_df["sum_ai_daily"].rolling(window=7).mean()
4つの空港すべての正規化された日次アクティビティ指標の合計の7日間のローリング平均をプロットします:
グラフ5:サンフランシスコ国際空港、デンバー国際空港、ダラス/フォートワース国際空港、およびダレス国際空港における2020年1月1日から12月31日までの正規化された1日の総活動量の7日間移動平均。すべての系列は、2020年1月4日から1月31日までの総活動量に正規化されています。出典:Mapbox Movementデータ。 7日間の移動平均は、2020年4月12日のパンデミックによる閉鎖のピークから、4つの空港すべての夏と冬の数ヶ月間のマクロな変化をより容易に観察するのに役立ちます。正規化後、DENが活動レベルで1位、DFWが2位であることがわかります。また、夏の再開段階でDENとDFWの活動レベルが上昇し、パンデミック前の約半分の活動レベルに達していることも容易に確認できます。
もちろん、私たちの分析は単純な移動平均に限定される必要はありません。他の時系列分析手法を適用して、系列を平滑化することもできます。たとえば、曜日ごとの変動で正規化したり、平日と週末の交通量を個別に分析したりすることもできます。詳細については、付録のセクションIIをご覧ください。
ちょっと待ってください!これらの空港は、半径1 kmの円 よりもずっと大きくありませんか? さて、部屋の中の象、つまり、空港の規模や旅客数に関係なく、各空港の周囲に半径1 kmの円を描いたという事実に対処しましょう。FAAによると、SFOは5,207エーカー(21.07平方キロメートル)を占め、DENは33,531エーカー(52.4平方マイル、135.7平方キロメートル)をカバーしています。旅客数に関しては、デンバー国際空港(DEN)は2018年に記録的な6,450万人の乗客を記録しましたが、SFOは同じ年に5,770万人以上の旅行者を記録しました。同じサイズの円を描くことは、各空港の周囲のエリアをすばやく定義するための便利な最初のステップでしたが、空港の真の活動パターンのどれだけを捉えることができたでしょうか?
簡単な答えは、正規化された日次集計アクティビティの傾向は非常に安定しているため、相対的なアクティビティの観点からはそれほど重要ではないということです。下のグラフは、デンバー国際空港(DEN)の正確に描かれた境界内での、さまざまな円のサイズと場所に対する正規化された集計日次アクティビティを示しています。
グラフ6:デンバー国際空港の正確に描かれた境界線内の正規化された1日の総活動量と、様々な場所にある円との比較:円の中心は元の中心座標から-0.01、-0.005、0.005、0.01度シフトしています。半径は1km、1.5km、2km、2.5km、4kmの範囲です。すべての系列は、2020年1月4日から1月31日までの総活動量に正規化されています。出典:Mapbox Movementデータ。 空港の追加:JFK、LGA、EWR 上記の方法により、関心のある空港に拡張できます。下のグラフは、ジョン・F・ケネディ国際空港(JFK)、ラガーディア空港(LGA)、ニューアーク・リバティー国際空港(EWR)の3つの新しい空港の、正規化された集計された1日のアクティビティの7日間の移動平均を示しています。これら3つの空港は互いに近接しています。3つの空港すべてのアクティビティは、パンデミックの開始後、以前の4つの空港で見られたものと同様の減少パターンを示しています。ニューアーク・リバティー国際空港とラガーディア空港のアクティビティレベルは、夏季の再開段階で上昇傾向を示していますが、ジョン・F・ケネディ国際空港のアクティビティは比較的低いままです。JFK空港は地下鉄に直接接続されており、3つの空港の中で最も多くの国内線および国際線と時間のオプションがあるため、これは特に興味深いことです。多くのニューヨーカーにとって、JFK空港は頼りになる空港です。JFKで見られるアクティビティレベルの抑制は、渡航禁止による国際線の前例のない減少に起因する可能性があります。
グラフ7:ジョン・F・ケネディ国際空港(JFK)、ラガーディア空港(LGA)、ニューアーク・リバティー国際空港(EWR)における2020年1月1日から12月31日までの正規化された1日の総活動量の7日間移動平均。すべての系列は、2020年1月4日から1月31日までの総活動量に正規化されています。出典:Mapbox Movementデータ。 結論—そして、さらなる可能性 このブログ記事では、Mapbox Movementデータにアクセスして最大限に活用するためのツールと方法論を調査しました。また、小さなエリア(1 kmの円)の正規化された日次集計活動指数が、空港全体の全体的な移動パターンをどの程度信頼できるかについても詳しく調べました。これは、この投稿で紹介したツールとデータセットを使用すると、マクロパターンを観察し、パンデミックによって最も深刻な影響を受けた業界をよりよく理解するための興味深い洞察を引き出すことができることを意味します。空港データセットに関連して調査する価値のある興味深いアイデアをいくつか紹介します。
Compare activity trends for small and medium-sized airports against large airports in the US. 2020年の感謝祭の頃、米国の中小規模の空港で興味深い空間的および時間的傾向が見られるかもしれません。Compare activity trends for airports in other countries around the world against US airports. Mapbox Movementはグローバルにカバーしています。これらの同じツールと方法論は、小売店、公園、ビーチなど、他の地理的な場所にも適用できます。このチュートリアルを試したり、独自のモビリティに関する洞察を得たりすることに興味がある場合は、Mapbox Movement Data page をチェックしてデータをダウンロードし、GitHub またはAWS S3 からJupyter notebookを入手して、今日から始めましょう!
付録 I. 投影法 投影法とは、球面座標(緯度と経度)をXY(平面)座標系に変換する数学的変換のことです。ただし、曲面で使用される座標系を平面で使用される座標系に変換するため、歪みなしに一方を他方に完全に変換する方法はありません。形状と面積を保持する投影法もあれば、距離と方向を保持する投影法もあります。したがって、投影法を決定することは簡単な作業ではありません。一般的に使用されるデフォルトは、ユニバーサル横メルカトル(UTM )で、地球を幅6度の60の定義済みゾーンに分割します。これらのゾーン内では、UTM投影法の歪みはごくわずかです。ただし、UTMゾーンの中心から離れるほど、面積と距離の歪みが大きくなります。この例では、米国全体の空港間の距離計算を実行しています。正距方位図法(AEQD)は、より適切な投影法です。地図上のすべての点が中心点から比例して正しい距離にあり、地図上のすべての点が中心点から正しい方向にあるという有用な特性があります。
II. すべてのZ18 QUADKEYについて、調整された1日の平均活動指数を計算する Mapbox Movement製品の以前の紹介で、アクティビティインデックスは、モバイルデバイスからの匿名化された位置データを集計することによって計算されると述べました。この匿名化プロセスは、Mapboxユーザーを保護するために実装され、毎日適用されます。このプロセス中に、小さなランダムノイズが意図的に合計アクティビティ数に適用されます。最小しきい値を下回るカウントの地理的領域は削除されます。これは、特定のズーム18クアッドキーが、特定の日においてプライバシーしきい値を超えるのに十分なアクティビティ数を持たない可能性があることを意味します。これらのクアッドキーは、その特定の日のデータセットには含まれません。
このため、中央値や平均値を計算する際には、特別な注意が必要です。組み込みの mean() および median() メソッドをアクティビティ数に単純に適用すると、分母(十分なアクティビティ数を持つクアッドキーの数に基づく)も日々変動するため、不正確な結果につながる可能性があります。代わりに、1日の平均を計算する正しい方法は、半径1 kmの円と重なるすべての Z18クアッドキーの総数で平均することです。これは、特定の日にプライバシーのしきい値を超えなかったZ18クアッドキーも考慮に入れるため、使用するのに適切な分母です。たとえば、サンフランシスコ国際空港(SFO)を例にとると、
# us_airports_df_large DataFrameからSFOを見つけます sfo = us_airports_df_large[us_airports_df_large["iata_code"] == "SFO"].iloc[0] # 1 kmの円ポリゴンと重なるz18クアッドキーの合計数を取得します total_num_z18_qks = float(len(set(sfo['z18_quadkeys']))) # 日ごとのアクティビティの合計を使用して、調整された日次平均アクティビティ指数を計算します。統計DataFrameに新しい列として追加します sfo_ai_stats_df['adj_mean_ai_daily'] = sfo_ai_stats_df['sum_ai_daily'] / total_num_z18_qks
4つの空港すべての調整済み日次平均アクティビティ指標をプロットします:
グラフ8:サンフランシスコ国際空港、デンバー国際空港、ダラス/フォートワース国際空港、およびダレス国際空港における2020年1月1日から12月31日までの調整された平均1日の総活動量。出典:Mapbox Movementデータ。 III. おおよその空港境界と正確な空港境界の詳細な比較 このセクションでは、空港の境界線をどのように定義するかに応じて、日次集計アクティビティ指標と正規化された曲線がどのように変動するかを検証します。狭いエリア(1 kmの円)の正規化された日次集計アクティビティ指標が、空港全体の全体的な移動パターンを適切に捉えていると信頼できるでしょうか?空港の正確に描かれた境界線から派生した境界線を含む、さまざまな境界線サイズの統計を比較します。
元の空港分析で行ったことと同様に、最初に新しい境界線を描画する必要があります。たとえば、z18_sample_data_sfo_df などのフィルタリングされたDataFrameは、それぞれの空港の元の1 km円ポリゴンと重複するズーム18クアッドキーからのデータのみで構成されているため、使用できなくなりました。より大きなズーム18クアッドキーのセットからのアクティビティデータが必要になります。ダラス/フォートワース国際空港(DFW)、デンバー国際空港(DEN)、ダレス国際空港(IAD)の4つの空港について、2020年1月1日から2020年12月31日までのアクティビティデータを圧縮したことを思い出してください。これらのzipファイルはそれぞれ、単一のズーム7クアッドキー内のすべて のズーム18クアッドキーからのアクティビティデータで構成されています。
付録のセクションIIIにある関数download_zipped_z7_sample_data_to_df.() を使用すると、特定の空港のzipファイルをダウンロードし、そのMovementサンプルデータを保持するためのPandas DataFrameを作成できます。このセクション全体を通して、デンバー国際空港(DEN)を例として使用します。まず、DENの2020年のMovementサンプルデータをダウンロードします。
# DEN Z7クアッドキーのサンプルデータをロードします。すべてのZ18クアッドキーからの利用可能なデータを含む、フィルタリングされていないDataFrameをロードすることに注意してください airports_sample_data_path = os.path.join(os.getcwd(), 'airports_sample_data') sample_data_df_den = download_zipped_z7_sample_data_to_df(airports_sample_data_path)
また、元の円の重心座標をどれだけシフトしたいか、円をどれだけ大きくしたいかという2つの要素に基づいて、新しい円ポリゴンオブジェクトのスイートを生成するのに役立つメソッドも必要です。Jupyter notebook の関数generate_new_circles.() は、入力された小数点以下の度数の範囲で、垂直、水平、左右の4方向に重心座標を一様にシフトし、これらの新しい重心座標を中心とした半径の入力範囲に基づいて円ポリゴンオブジェクトを作成します。
以下のスニペットは、DENに対して、中心位置と半径が異なる27個の新しい円を生成します。
# デンバー国際空港 den = us_airports_df_large[us_airports_df_large['name'].str.contains("Denver")].iloc[0] # 新しい円の中心をシフトする角度の範囲を設定します center_r = np.round(np.arange(-0.01, 0.015, 0.005), 3) # 新しい円の半径の範囲を設定します size_r = [1000, 1500, 2000] # DENの新しい円を生成します den_new_circles, den_new_circle_map = generate_new_circles(den, center_r, size_r)
これらは、kepler.glマップ上でこれらの円がどのように見えるかです。
デンバー国際空港のために描かれたすべての新しい円の地図。円の中心は、元の中心座標から-0.01、-0.005、0.005、0.01度シフトしています。半径は1 km、1.5 km、2 kmです。青色の中央の円は、半径1 kmの元の円です。 上記の4つの空港それぞれの統計を生成した方法と同様に、generate_daily_stats_circles.() 関数を使用して、これらの新しい円それぞれに対して同じプロセスを繰り返します。plot_daily_stats_curves.() 関数を使用してこれらの統計をプロットします(詳細なコードは、GitHub およびAWS S3 で入手できるJupyterノートブックの付録セクションにあります)。日次集計アクティビティインデックスと正規化された日次集計アクティビティインデックスがどのように変化するかを観察してください。
一部の円は、他の円よりも日次集計アクティビティが高いことがわかります。他の円よりも一貫して日次集計アクティビティが高い円は、元の中心座標(-104.672996521、39.861698150635)から南に0.01度シフトし、半径が2000メートル(「y_0.01_2000」とラベル付け)されている円です。
グラフ9:デンバー国際空港の様々な場所における1日の総活動量:円の中心は元の中心座標から-0.01、-0.005、0.005、0.01度シフトしています。半径は1km、1.5kmから2kmの範囲です。出典:Mapbox Movementデータ。 興味深いことに、正規化された曲線はすべて、中心の位置やカバーされる領域の量に関係なく、同様のパターンに従っています(プロットの凡例は、さまざまな半径を持つ元の中心座標の正規化された曲線を示しています)。
グラフ10:デンバー国際空港の様々な場所における正規化された1日の総活動量:円の中心は元の中心座標から-0.01、-0.005、0.005、0.01度シフトしています。半径のサイズは1km、1.5km、2kmです。すべての系列は、2020年1月4日から1月31日までの総活動量に正規化されています。出典:Mapbox Movementデータ。 次に、DENの正確な境界線を描き、その毎日の集計アクティビティと正規化された曲線が、さまざまな円によって生成されたものとどのように異なるかを見てみましょう。
デンバー国際空港(DEN)の正確な境界線 2020年3月から2020年12月まで、正確な境界の正規化された曲線(下のプロットでprec_normalized_sum_DEN_precise とラベル付け)は、さまざまな円によって生成された他のすべての正規化された曲線と比較して、一貫して最も低い値を示しています。これは、正確な境界がDENのより広い領域をカバーし、国際ターミナル付近の低い交通量を考慮に入れているためである可能性があります。ほとんどのヨーロッパ諸国との間の渡航制限は、2020年3月13日に発効しました。これらの正規化された曲線は同様のパターンを共有していますが、下のプロットでは、DENでの全体的な活動に対する渡航禁止の影響を確認できます。
グラフ11:デンバー国際空港の正確に描かれた境界線内の正規化された1日の総活動量と、おおよその境界線内の活動量との比較(様々な場所にある異なるサイズの円:円の中心は元の中心座標から-0.01、-0.005、0.005、0.01度シフトしています。半径は1km、1.5km、2kmの範囲です)。すべての系列は、2020年1月4日から1月31日までの総活動量に正規化されています。出典:Mapbox Movementデータ。 半径を4キロメートルに増やすとどうなるか見てみましょう。
デンバー国際空港のために描かれたすべての新しい円の地図。円の中心は、元の中心座標から-0.01、-0.005、0.005、0.01度シフトしています。半径のサイズは1 km、1.5 km、2 km、4 kmです。ベージュ色の中央の円は、半径1 kmの元の円です。 4000メートルの正規化された毎日の合計は、正確な境界の正規化された毎日の合計とほぼ重複しており、そのパターンは他の円の正規化された曲線と一致しています。
グラフ12:デンバー国際空港の正確に描かれた境界線内の正規化された1日の総活動量と、様々な場所にある円との比較:円の中心は元の中心座標から-0.01、-0.005、0.005、0.01度シフトしています。半径は1km、1.5km、2km、2.5km、4kmの範囲です。すべての系列は、2020年1月4日から1月31日までの総活動量に正規化されています。出典:Mapbox Movementデータ。 これらの実験から、主に2つの結論を導き出すことができます。
空港全体の全体的な移動パターンは、これらの正規化された日次集計アクティビティ指標によって十分に表されていると信頼できます。上記のさまざまなプロットは、日次集計アクティビティ指標の大きさは変動する可能性がありますが、正規化された日次集計アクティビティ指標の全体的な傾向は、実際には中心座標と半径の特定の選択に依存しない ことを示しています。それらはかなり安定しています。 折れ線グラフに見られるように、個々の活動レベルには多少のノイズが含まれている可能性があります。活動レベルのマクロで一貫したパターンを探しており、個々のデータポイントを過度に解釈することは避けるべきであることを理解することが重要です。たとえば、正規化された異なる曲線では、10月のアクティビティスコアに最大0.1の差があります。これは、信頼水準分析を行うか、パターンが非常に明確かつ一貫して保持されていることが確認されない限り、そのサイズの変更を分析において特に意味のあるものと見なすべきではないことを意味します。別の例として、小売業の回復に関するブログ記事で行った小売分析があります。そのブログ記事では、5000を超える小売店の正規化された活動指標を比較しました。Costcoの全体的な移動パターンは、Macy'sとは大きく異なって見えました。一方、AMCの指標はMacy'sと大きく重複していました。これは、最初のペアの差が、2番目のペアの差よりも、人々の活動パターンがどのように変化したかについて、より強力なシグナルを提供することを意味します。