Pandas json_normalizeでネストされたJSONをDataFrameに展開!データ分析を加速 🚀
Web APIから取得したデータや、一部のNoSQLデータベースの出力は、ネストされた(階層構造を持つ)JSON形式で提供されることがよくあります。このような複雑なJSONデータをPandasのDataFrameで効率的に扱うためには、そのままでは困難です。そこで活躍するのが、Pandasの**json_normalize()関数**です。
json_normalize()は、ネストされた辞書のリストをフラットなDataFrameに変換し、階層構造を列として展開してくれる強力なツールです。この記事では、json_normalize()の基本的な使い方から、実践的なオプションまでを詳しく解説します。
ネストされたJSONとは? 🤔
通常のJSONはキーと値のペアで構成されますが、ネストされたJSONでは、値の中にさらに辞書やリストが含まれています。
例: ユーザー情報の中に、住所(address)が別の辞書として、スキル(skills)がリストとしてネストされている場合。
[
{
"id": 1,
"name": "Alice",
"address": {
"street": "123 Main St",
"city": "Tokyo"
},
"skills": ["Python", "SQL"]
},
{
"id": 2,
"name": "Bob",
"address": {
"street": "456 Oak Ave",
"city": "Osaka"
},
"skills": ["Java"]
}
]
このような構造のJSONを直接pd.DataFrame()で読み込もうとすると、addressやskillsの列には辞書やリストがそのまま格納されてしまい、分析しにくい形になります。
json_normalize()の基本:ネストをフラット化 ✨
json_normalize()は、Pandas 1.0以降ではトップレベルのpandasモジュールに直接あります。辞書や辞書のリストを引数に取ります。
最もシンプルな使い方
ネストされた辞書のリストをjson_normalize()に渡すだけで、自動的にフラットなDataFrameに展開されます。
import pandas as pd
# ネストされたJSONデータ (辞書のリスト)
data = [
{
"id": 1,
"name": "Alice",
"address": {
"street": "123 Main St",
"city": "Tokyo"
},
"skills": ["Python", "SQL"]
},
{
"id": 2,
"name": "Bob",
"address": {
"street": "456 Oak Ave",
"city": "Osaka"
},
"skills": ["Java"]
}
]
# json_normalizeでDataFrameに変換
df_flat = pd.json_normalize(data)
print("--- フラット化されたDataFrame ---")
print(df_flat)
# 出力例:
# id name address.street address.city skills
# 0 1 Alice 123 Main St Tokyo [Python, SQL]
# 1 2 Bob 456 Oak Ave Osaka [Java]
ご覧の通り、addressのネストされたキー(street, city)が、元のキー名とドット(.)で連結された新しい列名として展開されています。
json_normalize()の便利なオプション 🛠️
json_normalize()は、より複雑なJSON構造に対応するための柔軟なオプションを提供します。
1. record_path:展開するリストを指定
JSONの最上位が辞書で、その中に展開したいレコードのリストが含まれている場合、record_pathでそのリストのパスを指定します。
import pandas as pd
# 例: 注文情報の中に商品リストがネストされている
order_data = [
{
"order_id": "A001",
"customer": "Alice",
"items": [
{"item_id": "P001", "price": 100},
{"item_id": "P002", "price": 200}
]
},
{
"order_id": "A002",
"customer": "Bob",
"items": [
{"item_id": "P003", "price": 300}
]
}
]
# 'items'リストを展開
df_items = pd.json_normalize(order_data, record_path='items')
print("--- 'items'リストを展開 ---")
print(df_items)
# 出力例:
# item_id price
# 0 P001 100
# 1 P002 200
# 2 P003 300
2. meta:元の階層の情報を保持する
record_pathでリストを展開した場合、元の親要素の情報を新しいDataFrameに含めたいことがあります。その際にmetaオプションを使います。
import pandas as pd
order_data = [
{
"order_id": "A001",
"customer": "Alice",
"items": [
{"item_id": "P001", "price": 100},
{"item_id": "P002", "price": 200}
]
},
{
"order_id": "A002",
"customer": "Bob",
"items": [
{"item_id": "P003", "price": 300}
]
}
]
# 'items'を展開し、'order_id'と'customer'をメタデータとして含める
df_items_with_meta = pd.json_normalize(order_data, record_path='items',
meta=['order_id', 'customer'])
print("\n--- 'items'展開 + 親要素のメタデータ ---")
print(df_items_with_meta)
# 出力例:
# item_id price order_id customer
# 0 P001 100 A001 Alice
# 1 P002 200 A001 Alice
# 2 P003 300 A002 Bob
3. errors='ignore' / 'raise':エラー処理
存在しないキーを指定した場合の挙動を制御します。
-
errors='raise'(デフォルト): キーが存在しない場合、エラーを発生させます。 -
errors='ignore': キーが存在しない場合、その列をNaNで埋めます。
import pandas as pd
incomplete_data = [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob", "email": "bob@example.com"} # emailがないレコードがある
]
# 'email'列が存在しない場合でもエラーにしない
df_ignore_errors = pd.json_normalize(incomplete_data, errors='ignore')
print("\n--- 存在しないキーを無視 ---")
print(df_ignore_errors)
# 出力例:
# id name email
# 0 1 Alice NaN
# 1 2 Bob bob@example.com
4. sep:展開された列名の区切り文字
デフォルトではドット(.)が使われますが、sepオプションで別の区切り文字を指定できます。
import pandas as pd
data_sep = [
{"user": {"name": "Charlie"}}
]
# 区切り文字をアンダースコアに変更
df_sep = pd.json_normalize(data_sep, sep='_')
print("\n--- 区切り文字を'_'に変更 ---")
print(df_sep)
# 出力例:
# user_name
# 0 Charlie
まとめ
Pandasのjson_normalize()関数は、ネストされたJSONデータをフラットなDataFrameに効率的に変換するための不可欠なツールです。
-
基本的なフラット化:
pd.json_normalize(data) -
リストの展開:
record_pathオプション -
親要素情報の保持:
metaオプション -
エラー処理:
errors='ignore' -
展開された列名の区切り文字:
sepオプション
これらのオプションを使いこなすことで、複雑なJSON構造を持つデータも、Pandasを使ってスムーズに分析プロセスに乗せることができるでしょう。Webデータ分析を行う際には、ぜひjson_normalize()を積極的に活用してください!


