2 个回答
在工作中需要处理嵌套数据(尤其是无模式的 MongoDB 日志等)或者是网络爬虫抓取下来的多层嵌套数据需要展平处理。
使用
json_normalize
函数将多层嵌套的Json数据展平到DataFrame可以方便地将原始数据进行清洗和预处理,以便进一步的分析和处理。
例如,可以使用DataFrame中的缺失值处理函数对缺失值进行处理,使用数据标准化函数进行数据标准化,使用数据分箱函数进行数据分箱等。这样可以方便地对Json数据进行处理,为后续的数据分析和建模做好准备。
如果将它放在 BigQuery 中则很容易通过使用 WITHIN 等的查询将其更改为矩阵形状。那么是否可以编写一个可以很好地完成它的库,Pandas 有一个名为 json_normalize 的 API。
工程案例
例如想构建个人导航页面,其中数据又不想一个一个复制的话,通过爬虫可以获得下面的json数据。
这里我们需要的结构是一级大类,二级栏目,网站名称,网站描述,网址链接以及网站图表。
但是这里不是一层+列表的关系,是一个多层的关系,常规的直接pd.Dataframe显然不是我们需要的。
因此需要一个API去操作进行解析,其实也可以自己通过遍历循环的方式用最原始的方式进行处理,但是没有必要浪费时间,直接使用 json_normalize 就可以完美的解决这个问题。
先来看一下最后处理的结果,通过数据展平的方式直接构建好了需要的DataFrame表单。
df_[df_["data.name"] == "内容分享"]
这个完整的代码也非常简单。
import json
import pandas as pd
from pandas.io.json import json_normalize
import requests
import time
with open("data.json",encoding="utf8") as f:
data = json.load(f)
df_ = json_normalize(data,
# 这里是你需要最后每个展平最小项展示的字段
record_path=['data','category','urls'],
meta=[
# 数据展平一级显示的字段
['data','name'],
# 数据展平二级显示的字段
['data','name','name']
errors='ignore')
既然数据整理好了直接爬虫抓一下icon图表直接就可以拿来用了。这里为了避免url错误直接使用回溯机制进行抓取。
for i in range(len(df)):
try:
url = df["favicon"][i]
title = df["title"][i]
html = requests.get(url)
print(url)
with open("jpg/{}.jpg".format(title),'wb') as f:
f.write(html.content)
except:
pass
json_normalize()
拆解的数据结构
一般来说我们遇见的数据结构分两种。
- 一般JSON对象:采用{}将键值对数据括起来,有时候会有多层{}。
- JSON对象列表:采用[]将JSON对象括起来,形成一个JSON对象的列表,JSON对象中同样会有多层{},也会有[]出现,形成嵌套列表。
源码解释
json_normalize(
data: 'dict | list[dict]',
record_path: 'str | list | None' = None,
meta: 'str | list[str | list[str]] | None' = None,
meta_prefix: 'str | None' = None,
record_prefix: 'str | None' = None,
errors: 'str' = 'raise',
sep: 'str' = '.',
max_level: 'int | None' = None,
)
官网翻译的有点问题,这里按照我的理解来描述一下。
- data : 字典或者字典列表,就是Dataframe能转换的数据。
- record_path : 最后每个展平最小项展示的字段即每个单元数据的最小项记录。
- meta : 路径列表,即数据展平的层级字段,需要显示几级就出几个列表。
- meta_prefix : 为路径列名增加前缀的设置,没啥用暂时。
- record_prefix : 为记录列名增加前缀的设置,没啥用暂时。
- errors : 错误处理有 "raise","ignore",怕自己搞不定直接选择后者忽略。
- sep : 嵌套记录将生成以sep分隔的名称,没啥用。不喜欢路径用.表示的自己替换。
- max_level : 处理数据字典的深度,一般不用设置,既然展平的话不管多少层都无视。
游戏日志数据展平
import numpy as np
import pandas as pd
from pandas.io.json import json_normalize
data_list = [{
'id': 1,
'name': 'john',
'equipment_status': [{
'equipment_id': 1,
'attack': 1000,
'defense': 1200,
'speed': 800,
'luck': 500,
}, {
'equipment_id': 2,
'attack': 1100,
'defense': 1300,
'speed': 900,
'luck': 600,
}, {
'id': 2,
'name': 'henry',
'equipment_status': [{
'equipment_id': 3,
'attack': 1200,
'defense': 1400,
'speed': 1000,
'luck': 700,
}, {
'equipment_id': 4,
'attack': 1300,
'defense': 1400,
'speed': 1000,
'luck': 700,
}, {
'equipment_id': 5,
'attack': 1400,
'defense': 1500,
'speed': 1100,
'luck': 800,
df = pd.DataFrame(data=data_list)
df
在 json_normalize 函数的 record_path 参数中指定一个列表或字典的列名,将生成一个扩展它们的数据框。
df = json_normalize(data_list, record_path='equipment_status')
df
虽然暂时变成了矩阵,缺无法知道是哪个用户的日志。因此在 meta 参数中,指定要保留的列列表,这次把 id 和 name 都留了。
df = json_normalize(
data_list,
record_path='equipment_status',
meta=['id', 'name']
df
使用Python进行数据分析时,你可以使用pandas库中的
json_normalize
函数将多层嵌套的JSON数据展平到DataFrame中。
首先,你需要使用Python的内置的
json
库将JSON字符串解析为Python字典。然后,你可以使用
json_normalize
函数将嵌套的字典展平到DataFrame中。
例如,假设你有以下多层嵌套的JSON数据:
{
"id": 1,
"name": "John Smith",
"address": {
"street": "123 Main St",
"city": "New York",
"state": "NY"
"orders": [
"product": "Widget",
"quantity": 2,
"price": 19.99
"product": "Gadget",
"quantity": 1,
"price": 29.99
你可以使用以下代码将其展平到DataFrame中:
import json
import pandas as pd
# Parse the JSON string into a Python dictionary
data = json.loads(json_data)
# Flatten the dictionary into a DataFrame
df = pd.json_normalize(data)
# Print the resulting DataFrame
print(df)
这会输出以下DataFrame:
id name orders \
0 1 John Smith [{'product': 'Widget', 'quantity': 2, 'price':...
address.street address.city address.state