55人参与 • 2026-03-26 • Javascript
在数据分析实践中,我们经常需要处理来自 rest api、日志系统或 nosql 数据库(如 mongodb)的嵌套 json 数据。这类数据结构灵活,但往往包含多层嵌套,例如用户信息中嵌套地址,同时关联多个订单记录。直接将这样的数据导入 pandas 通常会得到一列包含字典或列表的“半成品” dataframe,无法直接用于分析。
虽然 pandas 提供了 pd.json_normalize() 等工具来展开嵌套结构,但在处理一对多关系(如一个用户对应多个订单)时,容易产生大量重复字段,导致内存膨胀和计算效率下降。本文将通过一个典型三层嵌套 json 示例,系统讲解如何合理展开嵌套数据,并根据分析目标选择最优策略,避免不必要的冗余。
考虑如下数据结构:
data = [
{
"user_id": 1,
"name": "alice",
"profile": {
"age": 30,
"address": {
"city": "beijing",
"country": "china"
}
},
"orders": [
{"order_id": "o1", "amount": 100},
{"order_id": "o2", "amount": 200}
]
},
{
"user_id": 2,
"name": "bob",
"profile": {
"age": 25,
"address": {
"city": "shanghai",
"country": "china"
}
},
"orders": [
{"order_id": "o3", "amount": 150}
]
}
]目标是将每个订单作为一行,同时附带用户的基本信息,形成如下表格:
| user_id | name | age | city | country | order_id | amount |
|---|---|---|---|---|---|---|
| 1 | alice | 30 | beijing | china | o1 | 100 |
| 1 | alice | 30 | beijing | china | o2 | 200 |
| 2 | bob | 25 | shanghai | china | o3 | 150 |
乍看之下,这似乎是一个简单的扁平化任务。但若某个用户有成百上千个订单,其基本信息将在每一行重复出现,造成显著的数据冗余。
pandas 的 pd.json_normalize() 是处理嵌套 json 的核心工具。它能自动将 a.b.c 形式的路径展开为列名:
import pandas as pd df = pd.json_normalize(data) # 列包括:user_id, name, profile.age, profile.address.city, ...
然而,orders 字段是一个字典列表,仍以列表形式存在。需进一步展开:
# 保留非 orders 字段
df_base = df.drop(columns=['orders'])
# 展开 orders
df_orders = df[['user_id', 'orders']].explode('orders').reset_index(drop=true)
df_orders_flat = pd.json_normalize(df_orders['orders'])
# 合并
result = pd.concat([
df_base.loc[df_base.index.repeat(df['orders'].apply(len))].reset_index(drop=true),
df_orders_flat
], axis=1)
此方法可得到所需宽表,但如前所述,用户信息被重复复制。若订单数量庞大,这种冗余将带来以下问题:
因此,是否展开应取决于分析目标,而非技术便利性。
此时需要将用户属性“附着”到订单上,展开是合理的。但可通过以下方式优化:
for col in ['name', 'city', 'country']:
result[col] = result[col].astype('category')
这可将内存占用降低 50% 以上,尤其适用于高基数分类变量。
city 而非完整 address)。此时不应展开订单。更优做法是构建两个独立表,模仿星型模型:
用户表(users):
| user_id | name | age | city | country |
|---|
订单表(orders):
| user_id | order_id | amount |
|---|
实现方式:
# 用户表
users = pd.json_normalize(data)[[
'user_id', 'name', 'profile.age',
'profile.address.city', 'profile.address.country'
]]
users.columns = ['user_id', 'name', 'age', 'city', 'country']
# 订单表
orders_list = []
for user in data:
for order in user['orders']:
orders_list.append({
'user_id': user['user_id'],
'order_id': order['order_id'],
'amount': order['amount']
})
orders = pd.dataframe(orders_list)
后续分析时按需关联:
# 例如:计算各城市订单总额
merged = orders.merge(users[['user_id', 'city']], on='user_id')
summary = merged.groupby('city')['amount'].sum()
这种方式避免了冗余,且便于维护和扩展。
当数据量超出单机内存限制时,建议:
user_id 分区,减少 i/o 开销。一个常见误区是认为“所有数据都必须变成宽表才便于分析”。实际上,展开是一种分析前的数据准备手段,不是存储规范。
在实际项目中,推荐的做法是:
此外,手写循环解析虽直观,但难以处理缺失字段、类型不一致等问题,且不可复用。相比之下,json_normalize + explode 的组合更具健壮性和扩展性。
处理嵌套 json 时,pandas 提供了强大而灵活的工具链,但关键在于理解数据语义与分析目标之间的关系:
最终,高效的数据处理不在于“能否展开”,而在于“何时展开、展开多少、如何管理冗余”。掌握这一思维,才能在复杂数据结构面前游刃有余。
以上就是pandas合理展开嵌套json数据的全过程的详细内容,更多关于pandas展开嵌套json数据的资料请关注代码网其它相关文章!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论