关于pandas:你必须知道的Pandas-解析json数据的函数jsonnormalize

前言: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设置为23,则输入后果应别离是什么?请自行尝试~

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='-&gt;')

输入后果为:

6. 为嵌套列表数据和元数据增加前缀

3例的输入后果中,各列名均无前缀,例如name这一列不知是元数据解析失去的数据,还是通过student嵌套列表的的出的数据,因而为record_prefixmeta_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-&gt;',
                  meta_prefix='meta-&gt;',
                  sep='-&gt;')

本例中,为嵌套列表数据增加students-&gt;前缀,为元数据增加meta-&gt;前缀,将嵌套key之间的分隔符批改为-&gt;,输入后果为:

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': '&lt;3级'},
  {<!-- -->'date': '2021-08-10',
   'wea': '雷阵雨',
   'wea_img': 'yu',
   'tem_day': '32',
   'tem_night': '27',
   'win': '无继续风向',
   'win_speed': '&lt;3级'},
  {<!-- -->'date': '2021-08-11',
   'wea': '雷阵雨',
   'wea_img': 'yu',
   'tem_day': '31',
   'tem_night': '27',
   'win': '无继续风向',
   'win_speed': '&lt;3级'},
  {<!-- -->'date': '2021-08-12',
   'wea': '多云',
   'wea_img': 'yun',
   'tem_day': '33',
   'tem_night': '27',
   'win': '无继续风向',
   'win_speed': '&lt;3级'},
  {<!-- -->'date': '2021-08-13',
   'wea': '多云',
   'wea_img': 'yun',
   'tem_day': '33',
   'tem_night': '27',
   'win': '无继续风向',
   'win_speed': '&lt;3级'},
  {<!-- -->'date': '2021-08-14',
   'wea': '多云',
   'wea_img': 'yun',
   'tem_day': '32',
   'tem_night': '27',
   'win': '无继续风向',
   'win_speed': '&lt;3级'},
  {<!-- -->'date': '2021-08-15',
   'wea': '多云',
   'wea_img': 'yun',
   'tem_day': '32',
   'tem_night': '27',
   'win': '无继续风向',
   'win_speed': '&lt;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-&gt;',
                        meta_prefix='meta-&gt;',
                        sep='-&gt;')
df2 = pd.json_normalize(json_obj, record_path=['students'],
                        meta=['school', 'location', ['info', 'contacts', 'tel'],
                              ['info', 'contacts', 'email', 'general']],
                        record_prefix='students-&gt;',
                        meta_prefix='meta-&gt;',
                        sep='-&gt;')
# 将两个后果依据index关联起来并去除反复列
df1.merge(df2, how='left', left_index=True, right_index=True, suffixes=['-&gt;', '-&gt;']).T.drop_duplicates().T

输入后果为:

途中红框标出来的局部为Json对象中所对应的两个嵌套列表。

总结

json_normalize()办法异样弱小,简直涵盖了所有解析JSON的场景,波及到一些更简单场景时,能够给予已有的性能进行发散整合,例如8. 探索中遇到的问题一样。

领有了这个弱小的Json解析库,当前再也不怕遇到简单的Json数据了!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理