S3にあるparquetファイルのメタデータのみにアクセスしてスキーマ情報を取得する

2024.05.09

データアナリティクス事業本部のueharaです。

今回は、S3にあるparquetファイルのメタデータのみにアクセスしてスキーマ情報を取得してみたいと思います。

はじめに

S3にデータ容量の大きなparquetファイルがあり、例えばLambda内でそのparquetファイルのスキーマ情報を取得したい際にデータを丸ごとLambdaに持ってくるのは不都合な場合があります。

そこで、S3にあるparquetファイルのメタデータのみにアクセスしてスキーマ情報を取得してみたいと思います。

parquetファイルの構造

parquet-formatを見ると、parquetファイルの構造は以下のようになっております。

参考

これを見ると、Footer情報にメタデータが存在し、実際のFooter情報の長さは最後の8バイトの内前半の4バイトに保存されており、後半の4バイトは PAR1 というマジックナンバーとなっていることが分かります。

なお、Footer情報の長さはリトルエンディアンのようです。

boto3ではバイトレンジを指定してS3にあるオブジェクトのデータを取得することができるので、それを活用してメタデータのみ取得したいと思います。

やってみた

今回、テストに利用するparquetファイルはSample Parquet datasets for downloadWeather.parquet にします。

作成したPythonスクリプトは以下の通りです。

test.py

import io

import boto3
from pyarrow import parquet

# S3オブジェクトの情報を設定
s3_bucket_name = "cm-da-uehara"
s3_key = "tmp/Weather.parquet"

# S3クライアントの初期化
s3 = boto3.client("s3")

# ファイルの最後の8バイトを取得して、フッタの長さを取得
last_bytes = s3.get_object(Bucket=s3_bucket_name, Key=s3_key, Range="bytes=-8")[
    "Body"
].read()
footer_length = int.from_bytes(last_bytes[:4], "little")
magic = last_bytes[4:]
# 想定する構造になっているか確認
assert magic == b"PAR1", "Invalid parquet file"

# フッタの長さを使用して実際のフッタ情報を取得
footer_range = f"bytes=-{footer_length + 8}-"
footer_bytes = s3.get_object(Bucket=s3_bucket_name, Key=s3_key, Range=footer_range)[
    "Body"
].read()

# フッタ情報のBytesIOオブジェクトを作成
footer_stream = io.BytesIO(footer_bytes)

# メタデータの取得
parquet_metadata = parquet.read_metadata(footer_stream)
schema = parquet_metadata.schema.to_arrow_schema()

# カラム名とデータ型の表示
for field in schema:
    print(f"{field.name}: {field.type}")

各処理でやっていることはコメントに記載している通りです。

上記スクリプトを実行すると、結果は以下の通りでした。

$ python test.py
MinTemp: double
MaxTemp: double
Rainfall: double
Evaporation: double
Sunshine: string
WindGustDir: string
WindGustSpeed: string
WindDir9am: string
WindDir3pm: string
WindSpeed9am: string
WindSpeed3pm: int64
Humidity9am: int64
Humidity3pm: int64
Pressure9am: double
Pressure3pm: double
Cloud9am: int64
Cloud3pm: int64
Temp9am: double
Temp3pm: double
RainToday: string
RISK_MM: double
RainTomorrow: string

きちんとスキーマ情報が取得できていることが分かります。

最後に

今回は、S3にあるparquetファイルのメタデータのみにアクセスしてスキーマ情報を取得してみました。

参考になりましたら幸いです。

参考文献