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()
を積極的に活用してください!