讲师中心 微信公众号

工具推荐

视频效率加速
updream

一键生成高质量推广成片

AniShort
AniShort

AI短剧协同制作平台

立刻MV
立刻MV

低成本歌曲 MV、视频生成

剧云
剧云

中文短剧剧本制作平台

Python Pandas:深度解析多层嵌套JSON数据的扁平化处理

轻杰姑娘_2680

轻杰姑娘_2680

发布时间:2025-09-22 08:48:32

|

348人浏览过

|

来源于php中文网

原创

python pandas:深度解析多层嵌套json数据的扁平化处理

本文详细介绍了如何使用Python Pandas库有效地将多层嵌套的复杂JSON数据扁平化为单一的表格结构。通过结合json_normalize函数的record_path、meta参数,以及后续的数据重塑操作(如explode和列名处理),本教程提供了一种将深层嵌套信息提取并整合到一行的专业方法,旨在帮助用户高效地处理非结构化数据,实现数据分析的便利性。

引言:复杂JSON数据扁平化的挑战

JSON(JavaScript Object Notation)因其灵活的结构和易于人机阅读的特性,已成为现代数据交换和存储的主流格式。然而,当JSON数据包含多层嵌套的对象或数组时,在进行数据分析或导入关系型数据库时,往往需要将其扁平化为二维表格结构。Pandas库中的json_normalize函数是处理此类任务的强大工具,但面对深度嵌套和混合结构(如字典中包含列表,列表中又包含字典),其使用方法需要细致的规划和额外的处理步骤。

核心工具:pandas.json_normalize 详解

pandas.json_normalize函数是专门为将半结构化JSON数据转换为扁平DataFrame而设计的。理解其核心参数是成功处理复杂JSON的关键。

  1. data: 要进行扁平化的JSON数据,可以是单个JSON对象(字典)或JSON对象列表。
  2. record_path: 这是一个关键参数,用于指定JSON中包含记录列表的路径。json_normalize会遍历这个路径下的列表,并将每个列表项(通常是字典)作为DataFrame的一行。
    • 它可以是一个字符串(例如 'records'),表示顶层键是一个记录列表。
    • 它也可以是一个列表(例如 ['parent_key', 'nested_list_key']),表示要深入到多层结构中查找记录列表。
    • 注意:record_path的目标必须是一个列表,其内部元素才是要被扁平化的“记录”。
  3. meta: 用于指定要从JSON的非record_path部分提取的元数据。这些元数据会被添加到扁平化后的每一行中。
    • 可以是单个字符串或字符串列表,表示顶层键(例如 ['id', 'name'])。
    • 可以是嵌套列表,用于从父级对象中提取嵌套键(例如 ['parent_key', 'nested_key'])。这在需要从record_path的“兄弟”路径或“祖先”路径中提取信息时非常有用。
  4. sep: 用于连接扁平化后新列名的分隔符(默认为.)。

实践案例:扁平化多层嵌套JSON数据

我们以以下复杂的JSON数据为例,目标是将其转换为一个包含所有关键信息的单行扁平表格。

import pandas as pd
import json

data = {
  "id": 12345,
  "name": "Doe",
  "gender": {
    "textEn": "Masculin"
  },
  "professions": [
    {
      "job_description": {
        "textEn": "Job description"
      },
      "cetTitles": [
        {
          "cetTitleType": {
            "textEn": "Recognition"
          },
          "issuanceDate": "1992-04-14T00:00:00Z",
          "phoneNumbers": [
            "123 221 00 70"
          ]
        }
      ]
    }
  ]
}

期望的输出格式如下:

立即学习“Python免费学习笔记(深入)”;

id name gender job_description cetTitleType issuanceDate phoneNumbers
12345 Doe Masculin Job description Recognition 1992-04-14T00:00:00Z 123 221 00 70

步骤一:初步扁平化与元数据提取

首先,我们需要确定最深层且需要展开的列表。在这个例子中,"professions" 是一个列表,其内部的每个职业对象又包含一个 "cetTitles" 列表。因此,record_path 应设置为 ["professions", "cetTitles"]。

同时,我们需要提取其他层级的元数据:

  • 顶层信息:"id", "name"。
  • 顶层嵌套字典:"gender"。
  • "professions" 列表内部的嵌套字典:["professions", "job_description"]。
# 步骤一:使用 json_normalize 进行初步扁平化和元数据提取
df = pd.json_normalize(
    data=data,
    record_path=["professions", "cetTitles"],
    meta=["id", "name", "gender", ["professions", "job_description"]]
)

print("初步扁平化后的DataFrame:")
print(df.to_string())
# 此时的df可能包含如下列(部分):
# issuanceDate                phoneNumbers cetTitleType.textEn     id name                  gender professions.job_description
# 1992-04-14T00:00:00Z  ['123 221 00 70']          Recognition  12345  Doe  {'textEn': 'Masculin'}    {'textEn': 'Job description'}

解释:

Expanso yaml-to-json
Expanso yaml-to-json

使用 Expanso Edge 管道将 YAML 输入转换为 JSON 格式。

下载
  • record_path=["professions", "cetTitles"] 告诉json_normalize去data['professions']的每个元素中寻找'cetTitles'列表,并将其内容展开。
  • meta 参数则负责将id、name(顶层),gender(顶层字典),以及professions列表内部的job_description(虽然job_description不是cetTitles的直接父级,但通过["professions", "job_description"]路径,json_normalize能智能地从professions的每个元素中提取它,并作为元数据附加到cetTitles的每个记录上)作为列添加到结果中。

步骤二:处理列表字段

phoneNumbers 字段在JSON中是一个列表。如果一个cetTitles记录可能包含多个电话号码,并且我们希望每个号码都对应一行,就需要使用explode()函数。在本例中,只有一个号码,但为了通用性,explode仍然是处理列表字段的有效方法。

# 步骤二:处理 phoneNumbers 列表字段
df = df.explode(column="phoneNumbers")

print("\n处理 phoneNumbers 后的DataFrame:")
print(df.to_string())
# 此时 phoneNumbers 列变为字符串 '123 221 00 70'

解释:explode()函数会将DataFrame中指定列的列表值展开为多行。如果phoneNumbers列包含['123', '456'],explode()会将其拆分为两行,其他列的数据保持不变。

步骤三:解构嵌套字典字段

在步骤一中,gender和professions.job_description被提取为嵌套字典。例如,gender列的值是{'textEn': 'Masculin'}。我们需要提取这些字典中的'textEn'值。

# 步骤三:解构嵌套字典字段并重命名
# 处理 gender 字段
gender_df = pd.DataFrame(df.pop("gender").values.tolist())
df = df.join(gender_df)

# 处理 job_description 字段
job_description_df = pd.DataFrame(df.pop("professions.job_description").values.tolist())
df = df.join(job_description_df)

print("\n解构嵌套字典后的DataFrame:")
print(df.to_string())
# 此时 df 中会新增 'textEn' 列,我们需要对其进行重命名

解释:

  • df.pop("gender") 将gender列从df中移除并返回它。
  • .values.tolist() 将Series中的字典转换为Python列表。
  • pd.DataFrame(...) 将这个字典列表再次转换为一个DataFrame,其中字典的键('textEn')成为列名。
  • df.join(...) 将这个新的DataFrame与主DataFrame合并。对于job_description也进行同样的操作。

步骤四:清理和重命名列

经过以上步骤,可能会出现一些不理想的列名,例如cetTitleType.textEn或新生成的textEn。我们需要清理这些列名,并将其重命名为更具语义的名称。

# 步骤四:清理和重命名列
# 统一处理通过 '.' 连接的列名,取最后一个部分
df.columns = df.columns.str.split(".").str[-1]

# 对特定列进行语义化重命名
df = df.rename(columns={
    "textEn": "gender" # 第一次处理 gender 字段时,其内部的 'textEn' 变成了新的 'textEn' 列
})

# 再次重命名,因为 job_description 字段的 'textEn' 也被统一处理了
# 假设我们知道在这一步,新生成的 'textEn' 列实际上是 job_description 的内容
# 这里的处理需要根据实际的列名冲突情况进行调整
# 在本例中,由于 gender 的 textEn 已经被重命名,剩下的 textEn 便是 job_description 的
df = df.rename(columns={
    "textEn": "job_description"
})

# 对于 cetTitleType.textEn 已经被 df.columns.str.split(".").str[-1] 处理为 cetTitleType
# 如果希望保留原始的 textEn 语义,可以进一步调整
# 例如:cetTitleType.textEn 变成了 'textEn',而我们希望它是 'cetTitleType'
# 实际上,df.columns.str.split(".").str[-1] 已经将 cetTitleType.textEn 变成了 textEn
# 并且将其值设为了 Recognition。我们需要更精确地重命名
# 重新审视 df.columns.str.split(".").str[-1] 后的列名:
# ['issuanceDate', 'phoneNumbers', 'textEn', 'id', 'name', 'textEn', 'textEn']
# 这里的 textEn 有三个,需要根据原始来源进行区分。
# 更健壮的方法是分步重命名或在 join 时直接指定新列名。

# 考虑到原始答案的重命名逻辑,它假设了特定的顺序和结果。
# 我们可以模拟原始答案的重命名逻辑,它可能在 join 后直接重命名。
# 让我们使用一个更清晰的重命名策略:

# 重置列名以反映初始 json_normalize 后的状态
# df.columns = ['issuanceDate', 'phoneNumbers', 'cetTitleType.textEn', 'id', 'name', 'gender', 'professions.job_description']

# 重新执行步骤3和4,并优化重命名
df = pd.json_normalize(
    data=data,
    record_path=["professions", "cetTitles"],
    meta=["id", "name", "gender", ["professions", "job_description"]]
).explode(column="phoneNumbers")

# 提取 gender 字典,并将其 'textEn' 值作为 'gender' 列加入
gender_values = pd.DataFrame(df.pop("gender").values.tolist())
df = df.join(gender_values.rename(columns={"textEn": "gender_value"})) # 临时重命名,避免冲突

# 提取 job_description 字典,并将其 'textEn' 值作为 'job_description' 列加入
job_description_values = pd.DataFrame(df.pop("professions.job_description").values.tolist())
df = df.join(job_description_values.rename(columns={"textEn": "job_description_value"})) # 临时重命名

# 清理 cetTitleType.textEn 列名
df = df.rename(columns={"cetTitleType.textEn": "cetTitleType"})

# 最终重命名合并后的列
df = df.rename(columns={
    "gender_value": "gender",
    "job_description_value": "job_description"
})

print("\n最终扁平化后的DataFrame:")
print(df.to_string())

解释:

  • 原始答案的重命名策略是先将所有带.的列名取最后一部分,然后对可能重复的textEn进行特定重命名。这种方式在列名结构复杂时容易出错。
  • 更稳健的方法是在join新DataFrame时直接指定新列名,或者分步处理,避免列名冲突。
  • 上述优化后的代码先用临时列名避免冲突,最后再统一重命名。cetTitleType.textEn直接通过rename精确处理。

完整示例代码

将上述所有步骤整合,得到最终的解决方案代码:

import pandas as pd
import json

data = {
  "id": 12345,
  "name": "Doe",
  "gender": {
    "textEn": "Masculin"
  },
  "professions": [
    {
      "job_description": {
        "textEn": "Job description"
      },
      "cetTitles": [
        {
          "cetTitleType": {
            "textEn": "Recognition"
          },
          "issuanceDate": "1992-04-14T00:00:00Z",
          "phoneNumbers": [
            "123 221 00 70"
          ]
        }
      ]
    }
  ]
}

# 1. 使用 json_normalize 进行初步扁平化和元数据提取
#    - record_path 指定要展开的列表路径
#    - meta 指定要从不同层级提取的元数据
df = pd.json_normalize(
    data=data,
    record_path=["professions", "cetTitles"],
    meta=["id", "name", "gender", ["professions", "job_description"]]
)

# 2. 处理 phoneNumbers 列表字段:使用 explode 将列表元素展开为独立行
#    在本例中,每个记录只有一个电话号码,所以 explode 不会增加行数
df = df.explode(column="phoneNumbers")

# 3. 解构嵌套字典字段并重命名
#    - 提取 gender 字典,并将其 'textEn' 值作为 'gender' 列加入
gender_values = pd.DataFrame(df.pop("gender").values.tolist())
df = df.join(gender_values.rename(columns={"textEn": "gender_value"})) # 使用临时列名避免冲突

#

热门AI工具

更多
豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2295

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

3742

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

1236

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

4339

2025.09.10

Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

360

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

272

2026.01.31

Python数据分析与Pandas高级实战
Python数据分析与Pandas高级实战

本专题围绕 Python 数据分析展开,系统讲解 Pandas 的高级用法,包括数据清洗、透视表、时间序列分析以及多表合并与分组操作。通过实战案例,帮助开发者掌握高效处理与分析数据的方法,提高数据处理效率与分析能力。

871

2026.04.13

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1898

2023.08.03

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

40

2026.11.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 12.5万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn