json_normalize方法:轻松将嵌套JSON数据转换为扁平化DataFrame
在数据处理与分析中,JSON(JavaScript Object Notation)是一种常见的数据交换格式,因其结构灵活、可读性强被广泛应用,JSON数据常以嵌套结构存在(如字典套字典、列表套字典),直接使用传统方法处理往往效率低下,Python的pandas库提供了json_normalize()方法,专门用于将嵌套JSON数据“扁平化”为结构清晰的DataFrame,极大简化了数据预处理流程,本文将详细介绍json_normalize方法的原理、参数及实际应用场景,助你轻松驾驭复杂JSON数据。
初识json_normalize:为什么需要它?
假设我们有以下嵌套JSON数据,记录了用户及其订单信息:
[
{
"id": 1,
"name": "Alice",
"orders": [
{"order_id": "A001", "amount": 100, "date": "2023-01-01"},
{"order_id": "A002", "amount": 200, "date": "2023-01-05"}
]
},
{
"id": 2,
"name": "Bob",
"orders": [
{"order_id": "B001", "amount": 150, "date": "2023-01-03"}
]
}
]
若直接用pd.DataFrame()转换,会得到如下结果:orders列保留了嵌套的字典列表,无法直接分析订单金额、日期等字段,而json_normalize能自动展开嵌套结构,将订单信息拆分为独立列,生成更易处理的DataFrame。
json_normalize的基本用法
核心参数解析
json_normalize的核心功能是“扁平化”,其关键参数如下:
data:输入的JSON数据,可以是字典、字典列表或JSON字符串(会自动解析)。record_path:指定要展开的嵌套字段路径(列表形式),用于定位需要拆分成多行的记录。meta:指定需要保留的元数据字段(即不需要展开的顶级字段),这些字段会与展开后的记录对应。record_prefix:为展开后的列名添加前缀,避免列名冲突。errors:处理错误的方式,'raise'(默认,遇到错误报错)、'ignore'(忽略错误)、'warn'(警告后继续)。max_level:最大展开层级,防止过度嵌套导致列名过长。
示例1:简单嵌套JSON的扁平化
以开头的用户订单数据为例,假设数据已加载为Python列表data:
import pandas as pd
from pandas import json_normalize
data = [
{
"id": 1,
"name": "Alice",
"orders": [
{"order_id": "A001", "amount": 100, "date": "2023-01-01"},
{"order_id": "A002", "amount": 200, "date": "2023-01-05"}
]
},
{
"id": 2,
"name": "Bob",
"orders": [
{"order_id": "B001", "amount": 150, "date": "2023-01-03"}
]
}
]
# 展开orders列表,保留id和name作为元数据
df = json_normalize(
data,
record_path=['orders'], # 指定展开orders字段
meta=['id', 'name'] # 保留顶级字段id和name
)
print(df)
输出结果:
order_id amount date id name
0 A001 100 2023-01-01 1 Alice
1 A002 200 2023-01-05 1 Alice
2 B001 150 2023-01-03 2 Bob
可以看到,orders列表被拆分为多行,id和name作为元数据与每条订单记录对应,结构清晰。
示例2:多级嵌套与列名前缀
当JSON存在多级嵌套时,可通过record_path指定路径,并用record_prefix避免列名重复。
data = [
{
"id": 1,
"info": {
"name": "Alice",
"contact": {"email": "alice@example.com", "phone": "123456"}
},
"orders": [
{
"order_id": "A001",
"details": {"product": "Book", "price": 100}
}
]
}
]
# 展开orders.details,保留info.name作为元数据,并为details列添加前缀
df = json_normalize(
data,
record_path=['orders', 'details'], # 两级路径:orders -> details
meta=['info.name'], # 保留info.name
record_prefix='order_' # details字段列名前缀为order_
)
print(df)
输出结果:
order_product order_price info.name
0 Book 100 Alice
record_path=['orders', 'details']表示先定位到orders列表,再展开其中的details字段;record_prefix='order_'使product和price列名变为order_product和order_price,避免与元数据字段冲突。
示例3:处理非列表形式的嵌套字段
若嵌套字段是字典而非列表(即不需要拆分成多行),可直接通过meta指定路径:
data = [
{
"id": 1,
"name": "Alice",
"contact": {"email": "alice@example.com", "phone": "123456"},
"address": {"city": "New York", "street": "5th Ave"}
}
]
# 将contact和address作为元数据直接展开(无需record_path)
df = json_normalize(
data,
meta=[
'name',
'contact.email', # 用点号表示嵌套路径
'contact.phone',
'address.city',
'address.street'
]
)
print(df)
输出结果:
name contact phone contact.email address.city address.street
0 Alice {'email': 'alice@example.com', 'phone': '123456'} 123456 alice@example.com New York 5th Ave
contact和address作为字典被直接展开为列,无需record_path。
进阶技巧与常见问题
处理复杂嵌套:混合列表与字典
当JSON中同时存在列表嵌套和字典嵌套时,需合理组合record_path和meta。
data = [
{
"id": 1,
"name": "Alice",
"orders": [
{
"order_id": "A001",
"items": [
{"product": "Book", "price": 100},
{"product": "Pen", "price": 20}
],
"store": {"name": "Store A", "location": "NYC"}
}
]
}
]
# 展开orders.items,保留order_id、store.name和name
df = json_normalize(
data,
record_path=['orders', 'items'], # 两级路径:orders -> items
meta=[
'name',
['orders', 'order_id'], # 列表形式指定元数据路径(避免与record_path冲突)
['orders', 'store', 'name']
]
)
print(df)
输出结果:
product price name order_id store name
0 Book 100 Alice A001 Store A NYC
1 Pen 20 Alice A001 Store A NYC
通过meta使用列表形式指定路径(如['orders', 'order_id']),可避免与record_path的路径解析冲突。
处理缺失数据:errors参数的灵活运用
当JSON数据结构不一致时(如某些记录缺少record_path指定的字段),可通过errors参数控制处理方式:
data = [
{
"id": 1,
"name": "Alice",
"orders": [{"order_id": "A001", "amount": 100}]
},
{
"id": 2,
"name": "Bob"
# 缺少orders字段
}
]
# 默认会报错(KeyError),设为'ignore'则跳过Bob记录
df = json_normalize(
data,
record_path=['orders'],
meta=['id', 'name'],
errors='ignore'
)
print(df)
输出结果:
order_id amount id


还没有评论,来说两句吧...