使用Python数据分析进行数据预处理如何将多层嵌套Json数据展平到DataFrame?

关注者
2
被浏览
2,812

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