はじめに
GlueのDynamicFrameを使わない場合、スキーマを定義してデータを読み込みたいときってありますよね。
そんな時、スキーマがめちゃくちゃ長くなると、別ファイルに切り出したくなると思います。
その方法のうちの1つを試したので、共有します。
方法
準備するデータ
実際にロードするjsonのデータsample_data.json
と、カラム定義ファイルschema.json
を準備します。
今回は、これらのファイルをS3に格納しています。
sample_data.json
{"Name": {"First name": "Taro", "Last name": "Tanaka"}, "Age": 23}
{"Name": {"First name": "Hanako", "Last name": "Yamada"}, "Age": 18}
{"Name": {"First name": "Ichiro", "Last name": "Sato"}, "Age": 38}
schema.json
{
"fields": [
{
"metadata": {},
"name": "Name",
"nullable": true,
"type": {
"fields": [
{
"metadata": {},
"name": "First name",
"nullable": true,
"type": "string"
},
{
"metadata": {},
"name": "Last name",
"nullable": true,
"type": "string"
}
],
"type": "struct"
}
},
{
"metadata": {},
"name": "Age",
"nullable": true,
"type": "integer"
}
],
"type": "struct"
}
ロード
S3に、以下のようにデータファイルとスキーマ定義ファイルを格納します。
.
└── sample-bucket
├── data
│ └── sample_data.json
└── schema
└── schema.json
その状態で、以下のようなコードをGlueで実行します。
import boto3
import json
from pyspark.sql.types import IntegerType, StringType, StructField, StructType
s3 = boto3.resource('s3')
# schema定義ファイルを読み込む
obj = s3.Object('sample-bucket','schema/schema.json')
response = obj.get()
body = response['Body'].read()
json_dict = json.loads(body)
# jsonデータを読み込む
schema = StructType.fromJson(json_dict)
df_with_schema = spark.read.schema(schema) \
.json("s3://sample-bucket/data/")
メリット
- 別ファイルとしてスキーマ定義を記載できるので、めちゃくちゃ長いスキーマ定義でもコードがきれい!
- スキーマ定義とコードが分離されるので、スキーマ定義の変更があってもpythonファイルを更新しなくてよい!
- スキーマファイルのパスをジョブパラメータとすることで、ジョブを使いまわせる!
デメリット
- pythonに直接スキーマ定義を書くよりも、少し冗長な書き方になる。
- 直接pythonファイルにスキーマが書かれないため、ぱっと見でスキーマ定義が分からない。
- スキーマ定義を別ファイルとして作成するため、ファイル数が増加しやすい。
おわりに
メリデメ挙げてみましたが、スキーマ定義と実際のコードが分離されて疎になるのは嬉しいケースが多そうですね。
一度jsonとしてロードした後にparquetなどカラムと型が定義されるファイル形式で出力すると、その後のデータ分析がやりやすそうです。
参考
- 困ったときは公式ドキュメント
- 検索で最初に見つけたサイト