共计 9507 个字符,预计需要花费 24 分钟才能阅读完成。
前言:Json 数据介绍
Json 是一个利用及其宽泛的用来传输和替换数据的格局,它被利用在数据库中,也被用于 API 申请后果数据集中。
尽管它利用宽泛,机器很容易浏览且节俭空间,然而却不利于人来浏览和进一步做数据分析,因而通常状况下须要在获取 json 数据后,将其转化为表格格局的数据,以不便人来浏览和了解。
常见的 Json 数据格式有 2 种,均以键值对的模式存储数据,只是包装数据的办法有所差别:
a. 个别 JSON 对象
采纳 {}
将键值对数据括起来,有时候会有多层{}
b. JSON 对象列表
采纳 []
将JSON 对象
括起来,造成一个 JSON 对象的列表,JSON 对象中同样会有多层 {}
,也会有[]
呈现,造成 嵌套列表
这篇文章次要讲述 pandas
内置的 Json 数据转换方法json_normalize()
,它能够对以上两种 Json 格局的数据进行解析,最终生成DataFrame
,进而对数据进行更多操作。本文的次要解构如下:
- 解析一个最根本的 Json- 解析一个带有多层数据的 Json- 解析一个带有嵌套列表的 Json- 当
Key
不存在时如何疏忽零碎报错 - 应用sep
参数为嵌套 Json 的Key
设置分隔符 - 为嵌套列表数据和元数据增加前缀 - 通过 URL 获取 Json 数据并进行解析 - 探索:解析带有多个嵌套列表
的 Json
json_normalize()函数参数解说
在进行代码演示前先导入相应依赖库,未装置 pandas 库的请自行装置(此代码在 Jupyter Notebook 环境中运行)。
from pandas import json_normalize | |
import pandas as pd |
1. 解析一个最根本的 Json
a. 解析个别 Json 对象
a_dict = {<!-- --> | |
'school': 'ABC primary school', | |
'location': 'London', | |
'ranking': 2 | |
} | |
pd.json_normalize(a_dict) |
输入后果为:
b. 解析一个 Json 对象列表
json_list = [{<!-- -->'class': 'Year 1', 'student number': 20, 'room': 'Yellow'}, | |
{<!-- -->'class': 'Year 2', 'student number': 25, 'room': 'Blue'} | |
] | |
pd.json_normalize(json_list) |
输入后果为:
2. 解析一个带有多层数据的 Json
a. 解析一个有多层数据的 Json 对象
json_obj = {<!-- --> | |
'school': 'ABC primary school', | |
'location': 'London', | |
'ranking': 2, | |
'info': {<!-- --> | |
'president': 'John Kasich', | |
'contacts': {<!-- --> | |
'email': {<!-- --> | |
'admission': 'admission@abc.com', | |
'general': 'info@abc.com' | |
}, | |
'tel': '123456789', | |
} | |
} | |
} | |
pd.json_normalize(json_obj) |
输入后果为:
多层 key 之间应用点隔开,展现了所有的数据,这曾经解析了 3 层,上述写法和 pd.json_normalize(json_obj, max_level=3)
等价。
如果设置 max_level=1
,则输入后果为下图所示,contacts
局部的数据会集成了一列
如果设置 max_level=2
,则输入后果为下图所示,contacts
下的email
局部的数据会集成了一列
b. 解析一个有多层数据的 Json 对象列表
json_list = [ | |
{<!-- --> | |
'class': 'Year 1', | |
'student count': 20, | |
'room': 'Yellow', | |
'info': {<!-- --> | |
'teachers': {<!-- --> | |
'math': 'Rick Scott', | |
'physics': 'Elon Mask' | |
} | |
} | |
}, | |
{<!-- --> | |
'class': 'Year 2', | |
'student count': 25, | |
'room': 'Blue', | |
'info': {<!-- --> | |
'teachers': {<!-- --> | |
'math': 'Alan Turing', | |
'physics': 'Albert Einstein' | |
} | |
} | |
} | |
] | |
pd.json_normalize(json_list) |
输入后果为:
若别离将 max_level
设置为 2
和3
,则输入后果应别离是什么?请自行尝试~
3. 解析一个带有嵌套列表的 Json
json_obj = {<!-- --> | |
'school': 'ABC primary school', | |
'location': 'London', | |
'ranking': 2, | |
'info': {<!-- --> | |
'president': 'John Kasich', | |
'contacts': {<!-- --> | |
'email': {<!-- --> | |
'admission': 'admission@abc.com', | |
'general': 'info@abc.com' | |
}, | |
'tel': '123456789', | |
} | |
}, | |
'students': [{<!-- -->'name': 'Tom'}, | |
{<!-- -->'name': 'James'}, | |
{<!-- -->'name': 'Jacqueline'} | |
], | |
} | |
pd.json_normalize(json_obj) |
此例中 students
键对应的值是一个 列表
,应用[]
括起来。间接采纳上述的办法进行解析,则失去的后果如下:
students
局部的数据并未被胜利解析,此时能够为 record_path
设置值即可,调用形式为 pd.json_normalize(json_obj, record_path='students')
,在此调用形式下,失去的后果只蕴含了name
局部的数据。
若要减少其余字段的信息,则需为 meta
参数赋值,例如下述调用形式下,失去的后果如下:
pd.json_normalize(json_obj, record_path='students', meta=['school', 'location', ['info', 'contacts', 'tel'], ['info', 'contacts', 'email', 'general']])
4. 当 Key
不存在时如何疏忽零碎报错
data = [ | |
{<!-- --> | |
'class': 'Year 1', | |
'student count': 20, | |
'room': 'Yellow', | |
'info': {<!-- --> | |
'teachers': {<!-- --> | |
'math': 'Rick Scott', | |
'physics': 'Elon Mask', | |
} | |
}, | |
'students': [{<!-- --> 'name': 'Tom', 'sex': 'M'}, | |
{<!-- --> 'name': 'James', 'sex': 'M'}, | |
] | |
}, | |
{<!-- --> | |
'class': 'Year 2', | |
'student count': 25, | |
'room': 'Blue', | |
'info': {<!-- --> | |
'teachers': {<!-- --> | |
# no math teacher | |
'physics': 'Albert Einstein' | |
} | |
}, | |
'students': [{<!-- --> 'name': 'Tony', 'sex': 'M'}, | |
{<!-- --> 'name': 'Jacqueline', 'sex': 'F'}, | |
] | |
}, | |
] | |
pd.json_normalize( | |
data, | |
record_path =['students'], | |
meta=['class', 'room', ['info', 'teachers', 'math']] | |
) |
在 class
等于 Year 2
的 Json 对象中,teachers
下的 math
键不存在,间接运行上述代码会报以下谬误,提醒 math
键并不总是存在,且给出了相应倡议:Try running with errors='ignore'
。
增加 errors
条件后,从新运行得出的后果如下图所示,没有 math
键的局部应用 NaN
进行了填补。
pd.json_normalize( | |
data, | |
record_path =['students'], | |
meta=['class', 'room', ['info', 'teachers', 'math']], | |
errors='ignore' | |
) |
5. 应用 sep
参数为嵌套 Json 的 Key
设置分隔符
在 2.a
的案例中,能够留神到输入后果的具备多层 key
的数据列题目是采纳 .
对多层 key 进行分隔的,能够为 sep
赋值以更改分隔符。
json_obj = {<!-- --> | |
'school': 'ABC primary school', | |
'location': 'London', | |
'ranking': 2, | |
'info': {<!-- --> | |
'president': 'John Kasich', | |
'contacts': {<!-- --> | |
'email': {<!-- --> | |
'admission': 'admission@abc.com', | |
'general': 'info@abc.com' | |
}, | |
'tel': '123456789', | |
} | |
} | |
} | |
pd.json_normalize(json_obj, sep='->') |
输入后果为:
6. 为嵌套列表数据和元数据增加前缀
在 3
例的输入后果中,各列名均无前缀,例如 name
这一列不知是元数据解析失去的数据,还是通过 student
嵌套列表的的出的数据,因而为 record_prefix
和meta_prefix
参数别离赋值,即可为输入后果增加相应前缀。
json_obj = {<!-- --> | |
'school': 'ABC primary school', | |
'location': 'London', | |
'ranking': 2, | |
'info': {<!-- --> | |
'president': 'John Kasich', | |
'contacts': {<!-- --> | |
'email': {<!-- --> | |
'admission': 'admission@abc.com', | |
'general': 'info@abc.com' | |
}, | |
'tel': '123456789', | |
} | |
}, | |
'students': [{<!-- -->'name': 'Tom'}, | |
{<!-- -->'name': 'James'}, | |
{<!-- -->'name': 'Jacqueline'} | |
], | |
} | |
pd.json_normalize(json_obj, record_path='students', | |
meta=['school', 'location', ['info', 'contacts', 'tel'], ['info', 'contacts', 'email', 'general']], | |
record_prefix='students->', | |
meta_prefix='meta->', | |
sep='->') |
本例中,为嵌套列表数据增加 students->
前缀,为元数据增加 meta->
前缀,将嵌套 key 之间的分隔符批改为->
,输入后果为:
7. 通过 URL 获取 Json 数据并进行解析
通过 URL 获取数据须要用到 requests
库,请自行装置相应库。
import requests | |
from pandas import json_normalize | |
# 通过天气 API,获取深圳近 7 天的天气 | |
url = 'https://tianqiapi.com/free/week' | |
# 传入 url,并设定好相应的 params | |
r = requests.get(url, params={<!-- -->"appid":"59257444", "appsecret":"uULlTGV9", 'city':'深圳'}) | |
# 将获取到的值转换为 json 对象 | |
result = r.json() | |
df = json_normalize(result, meta=['city', 'cityid', 'update_time'], record_path=['data']) | |
df |
result 的后果如下所示,其中 data
为一个嵌套列表:
{<!-- -->'cityid': '101280601', | |
'city': '深圳', | |
'update_time': '2021-08-09 06:39:49', | |
'data': [{<!-- -->'date': '2021-08-09', | |
'wea': '中雨转雷阵雨', | |
'wea_img': 'yu', | |
'tem_day': '32', | |
'tem_night': '26', | |
'win': '无继续风向', | |
'win_speed': '<3 级'}, | |
{<!-- -->'date': '2021-08-10', | |
'wea': '雷阵雨', | |
'wea_img': 'yu', | |
'tem_day': '32', | |
'tem_night': '27', | |
'win': '无继续风向', | |
'win_speed': '<3 级'}, | |
{<!-- -->'date': '2021-08-11', | |
'wea': '雷阵雨', | |
'wea_img': 'yu', | |
'tem_day': '31', | |
'tem_night': '27', | |
'win': '无继续风向', | |
'win_speed': '<3 级'}, | |
{<!-- -->'date': '2021-08-12', | |
'wea': '多云', | |
'wea_img': 'yun', | |
'tem_day': '33', | |
'tem_night': '27', | |
'win': '无继续风向', | |
'win_speed': '<3 级'}, | |
{<!-- -->'date': '2021-08-13', | |
'wea': '多云', | |
'wea_img': 'yun', | |
'tem_day': '33', | |
'tem_night': '27', | |
'win': '无继续风向', | |
'win_speed': '<3 级'}, | |
{<!-- -->'date': '2021-08-14', | |
'wea': '多云', | |
'wea_img': 'yun', | |
'tem_day': '32', | |
'tem_night': '27', | |
'win': '无继续风向', | |
'win_speed': '<3 级'}, | |
{<!-- -->'date': '2021-08-15', | |
'wea': '多云', | |
'wea_img': 'yun', | |
'tem_day': '32', | |
'tem_night': '27', | |
'win': '无继续风向', | |
'win_speed': '<3 级'}]} |
解析后的输入后果为:
8. 探索:解析带有 多个嵌套列表
的 Json
当一个 Json 对象或对象列表中有超过一个嵌套列表时,record_path
无奈将所有的嵌套列表蕴含进去,因为它只能接管一个 key 值。此时,咱们须要先依据多个嵌套列表的 key
将 Json 解析成多个 DataFrame
,再将这些DataFrame
依据理论关联条件拼接起来,并去除反复值。
json_obj = {<!-- --> | |
'school': 'ABC primary school', | |
'location': 'shenzhen', | |
'ranking': 2, | |
'info': {<!-- --> | |
'president': 'John Kasich', | |
'contacts': {<!-- --> | |
'email': {<!-- --> | |
'admission': 'admission@abc.com', | |
'general': 'info@abc.com' | |
}, | |
'tel': '123456789', | |
} | |
}, | |
'students': [{<!-- -->'name': 'Tom'}, | |
{<!-- -->'name': 'James'}, | |
{<!-- -->'name': 'Jacqueline'} | |
], | |
# 增加 university 嵌套列表,加上 students, 该 JSON 对象中就有 2 个嵌套列表了 | |
'university': [{<!-- -->'university_name': 'HongKong university shenzhen'}, | |
{<!-- -->'university_name': 'zhongshan university shenzhen'}, | |
{<!-- -->'university_name': 'shenzhen university'} | |
], | |
} | |
# 尝试在 record_path 中写上两个嵌套列表的名字,即 record_path = ['students', 'university], 后果杯水车薪 | |
# 于是决定分两次进行解析,别离将 record_path 设置成为 university 和 students, 最终将 2 个后果合并起来 | |
df1 = pd.json_normalize(json_obj, record_path=['university'], | |
meta=['school', 'location', ['info', 'contacts', 'tel'], | |
['info', 'contacts', 'email', 'general']], | |
record_prefix='university->', | |
meta_prefix='meta->', | |
sep='->') | |
df2 = pd.json_normalize(json_obj, record_path=['students'], | |
meta=['school', 'location', ['info', 'contacts', 'tel'], | |
['info', 'contacts', 'email', 'general']], | |
record_prefix='students->', | |
meta_prefix='meta->', | |
sep='->') | |
# 将两个后果依据 index 关联起来并去除反复列 | |
df1.merge(df2, how='left', left_index=True, right_index=True, suffixes=['->', '->']).T.drop_duplicates().T |
输入后果为:
途中红框标出来的局部为 Json 对象中所对应的两个嵌套列表。
总结
json_normalize()
办法异样弱小,简直涵盖了所有解析 JSON 的场景,波及到一些更简单场景时,能够给予已有的性能进行发散整合,例如 8. 探索
中遇到的问题一样。
领有了这个弱小的 Json 解析库,当前再也不怕遇到简单的 Json 数据了!