
JSON(JavaScript Object Notation)因其灵活的结构和易于人机阅读的特性,已成为现代数据交换和存储的主流格式。然而,当JSON数据包含多层嵌套的对象或数组时,在进行数据分析或导入关系型数据库时,往往需要将其扁平化为二维表格结构。Pandas库中的json_normalize函数是处理此类任务的强大工具,但面对深度嵌套和混合结构(如字典中包含列表,列表中又包含字典),其使用方法需要细致的规划和额外的处理步骤。
pandas.json_normalize函数是专门为将半结构化JSON数据转换为扁平DataFrame而设计的。理解其核心参数是成功处理复杂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"]。
同时,我们需要提取其他层级的元数据:
# 步骤一:使用 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'}解释:
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' 列,我们需要对其进行重命名解释:
经过以上步骤,可能会出现一些不理想的列名,例如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())解释:
将上述所有步骤整合,得到最终的解决方案代码:
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 Pandas:深度解析多层嵌套JSON数据的扁平化处理的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号