🗺️

Overture Maps Foundationのデータを活用したい (2): Placesデータの抽出と可視化

2023/08/21に公開

はじめに

Overture Maps Foundationというものがあります。前回、位置データ (Placesデータ) をダウンロードし、中身を確認できたけど、うまくPandasのDataFrameに変換できなくて詰まっていました。

https://zenn.dev/takilog/articles/b2c588753fb3aa

こちらの記事では、データの整形をやってみた結果をまとめます。

データの再確認

Schemeの再確認

前回の記事でも言及した通り、データの中身は Scheme のレポジトリに情報が書かれています。今回は位置データの名前やカテゴリ・位置(緯度経度)などに興味があるということで、データを頑張って読み込んでいきます。

再度、yamlファイルを確認してみます。

https://github.com/OvertureMaps/schema/blob/main/schema/places/place.yaml

↑のyamlファイルに書かれたpropertiesを上から読んでいくと、以下のような情報が読めそうです。

  • 名前: properties -> names stringが入っていそう
  • カテゴリ: properties -> categories stringが入っていそう
  • 位置情報: geometry geojsonのPointで入っていそう (https://geojson.org/schema/Point.json)

具体的な値の確認

また、pyarrow を使ってデータを読み込んだ前回の例を思い出して列名を確認してみると、次のようなデータが読めそうなことがわかります。位置情報を見る際には、bounding box (bbox) も使えそうですね。

import pyarrow.parquet
# 20230725_... はダウンロードしたものの1つ
table = pyarrow.parquet.read_table("20230725_210643_00079_ayc64_01c760ca-02aa-4387-8b71-b2eaa6c7c700")
print(table.column_names)

# 実行結果
['id',
 'updatetime',
 'version',
 'names',
 'categories',
 'confidence',
 'websites',
 'socials',
 'emails',
 'phones',
 'brand',
 'addresses',
 'sources',
 'bbox',
 'geometry']

位置情報を確認するため、0行目のデータのgeometryとbboxを確認してみます。Python側で処理するときには、pyarrowのデータ型を通常のpythonのデータ型に戻すためにas_py()を使っています。

print(table["geometry"][0].as_py())

# 実行結果
b'\x01\x01\x00\x00\x00\x92!\xc7\xd63\xb4\x10\xc0d\xb1M*\x1a\x9bJ@'

print(table["bbox"][0].as_py())

# 実行結果
{'minx': -4.175979, 'maxx': -4.175979, 'miny': 53.211736, 'maxy': 53.211736}

ここまでの結果を見ると、geometry のデータは後処理が必要そうですが、bbox のデータはそのまま緯度経度 (x側が経度、y側が緯度です) として使えそうです。以降では、bboxの情報を使うことにします。

東京データ (島含まない) の作成

ダウンロードしてきたデータをテーブルで読むとき、名前・カテゴリ・緯度経度などを取得できそうなことが分かりました。

元データを活用する例として、全データについて東京都内(島を含まない)の位置データを抽出してみようと思います。東京都内の判定には、以前作ったshpファイルを用いて、緯度経度が領域に含まれるかどうかを判定することにします。

https://zenn.dev/takilog/articles/0d355bbb80f79848da8d

全体処理は以下の3ステップで実装しました。

  1. 各 parquet ファイルから東京都内の位置データのインデクスを取得する
  2. 各 parquet ファイルの残ったインデクスからCSVファイルを作成する
  3. 全データのCSVファイルを結合する

それぞれ簡単に説明します。

処理1. 各 parquet ファイルから東京都内の位置データのインデクスを取得する

まずは以下の処理によって行番号 (index) を保存します。

  • すべてのparquetファイルについて、各行の緯度経度を bbox から取得する
  • 取得した点がshpファイルの内部に含まれていれば保存する

あるファイルを処理し、残った位置情報から散布図を打った例はこちらです。だいたい東京都内のデータが抽出できていることが分かります。

処理2. 各 parquet ファイルの残ったインデクスからCSVファイルを作成する

どの行が都内のデータかを先に処理して保存したので、後はCSVファイルを作れば良いです。読み込んだデータから名前を取得する部分が雑な実装になってしまっていますが、このような処理を動かして待っていればそのうち終わります。

# 名前などにカンマが入ったり区切りが難しそうなので、""と;で装飾する
# 緯度経度はbboxの(x, y)で代用する
with codecs.open(fn_table, "w", encoding="utf-8") as ff:
    ff.write('"name";"cat";"lon";"lat"\n')
    for i in tqdm(idx):
        data_i = {}
        for j in table.column_names:
            data_i[j] = table[j][i].as_py()
        name_i = data_i["names"][0][1][0][0][1]
        cat_i = data_i["categories"]["main"]
        lon_i = data_i["bbox"]["minx"]
        lat_i = data_i["bbox"]["miny"]
        ff.write(f'"{name_i}";"{cat_i}";"{lon_i}";"{lat_i}"\n')

変な処理を書いた原因ですが、データがこのようなものだったからです (このような入れ子構造があるので、pyarrowの処理でpandasに変換できなかったわけですね)。

このあたりのパースはまともに書く方法がある気もします (考えてないです)。

処理3. 全データのCSVファイルを結合する

これは処理2で作った個別のCSVをpandasでconcatし、CSVに出力するだけです。

作成したデータ

全データを処理した結果、276739 行のCSVファイルを作ることができました 💪 散布図を描くと、以下のようになりました。

ついでに、カテゴリの上位を棒グラフで見てみます。

import pandas as pd
import matplotlib.pyplot as plt
plt.style.use("ggplot")

# 作成したデータが tokyo.csv
df = pd.read_csv("tokyo.csv")

f = plt.figure()
a = f.gca()
df["cat"].value_counts().nlargest(30).plot(kind="bar", ax=a)
plt.tight_layout()
plt.show()

作成したグラフです。Noneも残ってしまっていますが、そこそこカテゴリがついているように見えますね。日本食レストラン・カフェ・バー・レストランなどが上位にあるみたいですね。

レポジトリ

今回の記事で使ったソースなどはこちらに置きました。

https://github.com/cocomoff/Processing-Overture

Discussion