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

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的关键。

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

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

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

import pandas as pdimport jsondata = {  "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

12345DoeMasculinJob descriptionRecognition1992-04-14T00:00:00Z123 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'}

解释:

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 pdimport jsondata = {  "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"})) # 使用临时列名避免冲突#

以上就是Python Pandas:深度解析多层嵌套JSON数据的扁平化处理的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1373091.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月14日 12:53:36
下一篇 2025年12月14日 12:53:53

相关推荐

  • Python 数据分块处理大数据集

    分块处理大数据可避免内存溢出。使用pandas的chunksize参数可逐块读取大型CSV文件,适合聚合清洗;通过生成器可自定义分块逻辑,实现懒加载;结合joblib能并行处理独立数据块,提升计算效率。关键在于根据数据规模和任务选择合适策略,并及时释放内存、保存中间结果。 处理大数据集时,直接将整个…

    2025年12月14日
    000
  • Python 实战:个人理财可视化工具

    答案:Python通过Pandas和Plotly等库将分散的财务数据清洗、分类并可视化,帮助用户直观分析收支趋势、发现消费黑洞、追踪资产变化,从而提升财务掌控力。 Python能帮助我们构建强大的个人理财可视化工具,将复杂的财务数据转化为直观图表,帮助我们洞察收支模式,做出更明智的财务决策。这不仅仅…

    2025年12月14日
    000
  • Python 类中的私有属性与私有方法

    Python通过双下划线实现“私有”属性和方法,本质是名称混淆而非强制私有,目的是避免子类冲突并提示内部使用,体现“我们都是成年人”的设计哲学。 Python中所谓的“私有”属性和方法,其实并非像其他语言那样提供严格的访问控制。它更多是一种约定和一种巧妙的名称混淆(name mangling)机制,…

    2025年12月14日
    000
  • Linux 用户的 Python 环境搭建流程

    检查并升级 Python 版本,确保满足开发需求;2. 使用 venv 创建独立虚拟环境避免依赖冲突;3. 在虚拟环境中安装第三方包并导出依赖列表;4. 通过激活与退出环境及删除目录实现安全清理。 Linux 系统自带 Python,但为了开发需要,通常要配置独立且可控的 Python 环境。以下是…

    2025年12月14日
    000
  • Pandas数据框中按组比较相邻行数据并生成新列的教程

    本教程详细介绍了如何在Pandas数据框中,根据特定分组(如Race_ID),比较当前行C_k列的值与下一行adv列的值。我们将探讨两种高效的方法来找出满足条件的第一个C_k值,并将其填充到一个新列C_t中,同时处理无匹配项时的默认值设定,以实现复杂的跨行条件逻辑。 引言:问题背景与目标 在数据分析…

    2025年12月14日
    000
  • 利用Prisma扩展在NestJS中实现数据库操作后置钩子

    本教程将详细介绍如何在NestJS应用中,结合Prisma ORM,通过使用Prisma客户端扩展(Client Extensions)来实现数据库操作后的自定义逻辑执行,例如发送通知或更新缓存。这种方法能够有效解耦业务逻辑与副作用,提升代码的可维护性和扩展性,避免将后置处理代码直接嵌入到API接口…

    2025年12月14日
    000
  • 利用Prisma客户端扩展在NestJS中实现数据库操作后置逻辑

    本文探讨了在NestJS应用中,如何利用Prisma客户端扩展实现类似Django Signals的数据库操作后置钩子。通过拦截create、update或delete等数据库操作,开发者可以在数据持久化成功后执行自定义逻辑,如发送通知或更新缓存,从而避免将这些交叉关注点直接耦合在业务逻辑或API端…

    2025年12月14日
    000
  • NestJS与Prisma:实现数据库操作后的钩子与副作用处理

    本文探讨了在NestJS应用中结合Prisma ORM,如何在数据库记录创建、更新或删除后执行自定义业务逻辑,而无需将这些逻辑直接耦合到API层。针对类似Django Signals的需求,我们介绍了利用Prisma Client Extensions的query扩展功能,实现对数据库操作的拦截与增…

    2025年12月14日
    000
  • Python 延迟加载与按需计算

    延迟加载与按需计算通过推迟执行节省资源,利用属性、生成器和cached_property实现高效优化。 在 Python 中,延迟加载(Lazy Loading)和按需计算(On-demand Computation)是一种优化策略,用于推迟对象的创建或值的计算,直到真正需要时才执行。这种方式能有效…

    2025年12月14日
    000
  • python如何使用pillow库处理图片_python pillow图像处理库的基本操作

    Pillow是Python中处理图片的首选库,提供直观API,支持打开、编辑、保存等操作,适用于调整尺寸、裁剪、旋转、滤镜应用等常见任务。安装简单,通过pip install Pillow即可完成。核心模块为Image,常用功能包括:1. 打开并显示图片,支持格式、尺寸、模式查询及错误处理;2. 调…

    2025年12月14日
    000
  • python如何优雅地拼接字符串路径_python os.path.join拼接路径的正确方法

    最推荐使用os.path.join()或pathlib模块拼接路径,因它们能自动处理不同操作系统的分隔符差异并规范路径。os.path.join()是传统方法,可智能合并路径片段、避免重复斜杠,并在遇到绝对路径时重新开始拼接;而pathlib自Python 3.4引入,提供面向对象的现代语法,支持用…

    2025年12月14日
    000
  • python中如何自定义一个异常类?

    自定义异常类需继承Exception,可添加属性和方法以提供详细上下文信息。如InsufficientFundsError携带金额数据并重写__str__,提升错误可读性与处理精度。通过创建基类异常(如MyAppError)构建层次化结构,集中管理于exceptions.py,实现细粒度捕获与统一处…

    2025年12月14日
    000
  • Python 3.x 与 2.x 的差异与兼容性问题

    Python 3与2.x主要差异包括:1. print变为函数;2. 字符串默认Unicode,bytes分离;3. 除法返回浮点数;4. 模块重命名如urllib2拆分;5. 兼容建议用__future__导入和six库。 Python 3.x 与 2.x 存在显著差异,这些变化旨在提升语言的清晰…

    2025年12月14日
    000
  • python中__str__和__repr__方法有什么区别?

    __str__用于生成人类可读的字符串,适合展示给用户;__repr__则生成明确无歧义的开发者用字符串,理想情况下可重构对象。两者分工明确,建议优先定义__repr__以保障调试信息完整,再根据需要定义__str__提供友好显示。若只选其一,应优先实现__repr__。 在Python里, __s…

    2025年12月14日
    000
  • Snakemake规则在Slurm模式下Python输出实时显示与最佳实践

    在Snakemake的Slurm模式下,Python脚本的实时输出(如print()语句)可能因标准输出缓冲而延迟显示。本文将探讨导致此问题的原因,提供通过刷新标准输出来即时解决的方法,并重点介绍更深层次的Snakemake规则重构最佳实践,包括细化规则粒度、避免内部循环、优化输入/输出处理以及利用…

    2025年12月14日
    000
  • 如何解决 pip 安装库过慢的问题

    更换国内镜像源可显著提升pip安装速度,推荐使用清华、阿里云等镜像,通过临时-i参数或永久配置pip.ini/pip.conf实现,Linux/macOS还可设置别名;同时升级pip并启用缓存机制,必要时配置代理,综合运用使库安装更高效。 使用 pip 安装 Python 库时速度慢,通常是因为默认…

    2025年12月14日
    000
  • 高效对比Pandas DataFrame并提取差异数据

    本文详细介绍了如何利用Pandas库的DataFrame.compare()方法,高效地对比两个结构相似的DataFrame,并精确地提取出所有存在差异的行和列。教程将演示如何通过设置索引、调用compare()函数及后续的数据清洗步骤,最终生成一个仅包含差异数据及关键标识列的DataFrame,从…

    2025年12月14日
    000
  • Python 内存映射文件优化 mmap

    mmap通过将文件映射到内存,避免传统I/O的数据拷贝,适用于大文件或频繁随机访问;使用mmap.mmap创建映射后可像操作字符串一样读写数据,读取时按需加载页减少内存占用,写入时选择ACCESS_WRITE或ACCESS_COPY模式并注意flush和同步问题,适合GB级文件处理但不适用于小文件或…

    2025年12月14日
    000
  • python如何读取一个txt文件_python读写TXT文件的基本操作

    Python读写TXT文件需用open()函数配合with语句确保安全,读取可用read()、readline()或readlines(),写入用write()或writelines(),并指定编码防乱码。 Python读取TXT文件,核心在于使用内置的 open() 函数来打开文件,然后根据需求选…

    2025年12月14日
    000
  • python如何从网页上下载图片_python爬虫下载网页图片实战方法

    答案:用Python下载网页图片需三步:获取网页内容、解析提取图片链接、下载保存。先用requests加headers获取HTML,再用BeautifulSoup解析img标签,处理相对路径,最后通过requests获取二进制数据并保存文件。 用Python从网页上下载图片,说白了,这事儿的核心逻辑…

    2025年12月14日
    000

发表回复

登录后才能评论
关注微信