データアナリティクス事業本部の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 downloadの Weather.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ファイルのメタデータのみにアクセスしてスキーマ情報を取得してみました。
参考になりましたら幸いです。