0

0

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

霞舞

霞舞

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

|

308人浏览过

|

来源于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'}

解释:

TemPolor
TemPolor

AI音乐生成器,一键创作免版税音乐

下载
  • 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"})) # 使用临时列名避免冲突

#

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

741

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

634

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

755

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

617

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1259

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

577

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

705

2023.08.11

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

3

2026.01.09

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 3.5万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.1万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.8万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号