在 Python 中将嵌套 JSON 转换为 CSV 文件

2024-03-06

我知道这个问题已经被问过很多次了。我尝试了多种解决方案,但无法解决我的问题。

我有一个大的嵌套 JSON 文件 (1.4GB),我想将其扁平化,然后将其转换为 CSV 文件。

JSON结构是这样的:

{
  "company_number": "12345678",
  "data": {
    "address": {
      "address_line_1": "Address 1",
      "locality": "Henley-On-Thames",
      "postal_code": "RG9 1DP",
      "premises": "161",
      "region": "Oxfordshire"
    },
    "country_of_residence": "England",
    "date_of_birth": {
      "month": 2,
      "year": 1977
    },
    "etag": "26281dhge33b22df2359sd6afsff2cb8cf62bb4a7f00",
    "kind": "individual-person-with-significant-control",
    "links": {
      "self": "/company/12345678/persons-with-significant-control/individual/bIhuKnFctSnjrDjUG8n3NgOrl"
    },
    "name": "John M Smith",
    "name_elements": {
      "forename": "John",
      "middle_name": "M",
      "surname": "Smith",
      "title": "Mrs"
    },
    "nationality": "Vietnamese",
    "natures_of_control": [
      "ownership-of-shares-50-to-75-percent"
    ],
    "notified_on": "2016-04-06"
  }
}

我知道这很容易完成pandas模块,但我不熟悉它。

EDITED

所需的输出应该是这样的:

company_number, address_line_1, locality, country_of_residence, kind,

12345678, Address 1, Henley-On-Thamed, England, individual-person-with-significant-control

请注意,这只是简短的版本。输出应该包含所有字段。


请向下滚动以获得更新、更快的解决方案

这是一个较老的问题,但我整晚都在努力争取类似情况下的满意结果,我想出了这个:

import json
import pandas

def cross_join(left, right):
    return left.assign(key=1).merge(right.assign(key=1), on='key', how='outer').drop('key', 1)

def json_to_dataframe(data_in):
    def to_frame(data, prev_key=None):
        if isinstance(data, dict):
            df = pandas.DataFrame()
            for key in data:
                df = cross_join(df, to_frame(data[key], prev_key + '.' + key))
        elif isinstance(data, list):
            df = pandas.DataFrame()
            for i in range(len(data)):
                df = pandas.concat([df, to_frame(data[i], prev_key)])
        else:
            df = pandas.DataFrame({prev_key[1:]: [data]})
        return df
    return to_frame(data_in)

if __name__ == '__main__':
    with open('somefile') as json_file:
        json_data = json.load(json_file)

    df = json_to_dataframe(json_data)
    df.to_csv('data.csv', mode='w')

解释:

The 交叉连接函数是我发现做笛卡尔积的一种巧妙方法。 (信用:here https://stackoverflow.com/questions/53699012/performant-cartesian-product-cross-join-with-pandas)

The json_to_dataframe函数使用 pandas 数据框执行逻辑。就我而言,json 嵌套很深,我想拆分字典键:值对放入列中,但是我想将列表转换为列的行-- 因此是 concat -- 然后我将其与上层交叉连接,从而将记录数相乘,以便列表中的每个值都有自己的行,而前面的列是相同的。

递归性会创建与下面的堆栈交叉连接的堆栈,直到返回最后一个堆栈。

然后,使用表格格式的数据框,可以使用以下命令轻松转换为 CSV“df.to_csv()”数据框对象方法。

这应该适用于深度嵌套的 JSON,能够通过上述逻辑将所有 JSON 规范化为行。

我希望有一天这会对某人有所帮助。只是想回馈这个很棒的社区。

-------------------------------------------------- -------------------------------------------

稍后编辑:新解决方案

我回到这个话题,因为虽然 dataframe 选项有点工作,但应用程序花了几分钟来解析不太大的 JSON 数据。因此,我想到做数据框所做的事情,但由我自己来做:

from copy import deepcopy
import pandas


def cross_join(left, right):
    new_rows = [] if right else left
    for left_row in left:
        for right_row in right:
            temp_row = deepcopy(left_row)
            for key, value in right_row.items():
                temp_row[key] = value
            new_rows.append(deepcopy(temp_row))
    return new_rows


def flatten_list(data):
    for elem in data:
        if isinstance(elem, list):
            yield from flatten_list(elem)
        else:
            yield elem


def json_to_dataframe(data_in):
    def flatten_json(data, prev_heading=''):
        if isinstance(data, dict):
            rows = [{}]
            for key, value in data.items():
                rows = cross_join(rows, flatten_json(value, prev_heading + '.' + key))
        elif isinstance(data, list):
            rows = []
            for item in data:
                [rows.append(elem) for elem in flatten_list(flatten_json(item, prev_heading))]
        else:
            rows = [{prev_heading[1:]: data}]
        return rows

    return pandas.DataFrame(flatten_json(data_in))


if __name__ == '__main__':
    json_data = {
        "id": "0001",
        "type": "donut",
        "name": "Cake",
        "ppu": 0.55,
        "batters":
            {
                "batter":
                    [
                        {"id": "1001", "type": "Regular"},
                        {"id": "1002", "type": "Chocolate"},
                        {"id": "1003", "type": "Blueberry"},
                        {"id": "1004", "type": "Devil's Food"}
                    ]
            },
        "topping":
            [
                {"id": "5001", "type": "None"},
                {"id": "5002", "type": "Glazed"},
                {"id": "5005", "type": "Sugar"},
                {"id": "5007", "type": "Powdered Sugar"},
                {"id": "5006", "type": "Chocolate with Sprinkles"},
                {"id": "5003", "type": "Chocolate"},
                {"id": "5004", "type": "Maple"}
            ],
        "something": []
    }
    df = json_to_dataframe(json_data)
    print(df)

OUTPUT:

      id   type  name   ppu batters.batter.id batters.batter.type topping.id              topping.type
0   0001  donut  Cake  0.55              1001             Regular       5001                      None
1   0001  donut  Cake  0.55              1001             Regular       5002                    Glazed
2   0001  donut  Cake  0.55              1001             Regular       5005                     Sugar
3   0001  donut  Cake  0.55              1001             Regular       5007            Powdered Sugar
4   0001  donut  Cake  0.55              1001             Regular       5006  Chocolate with Sprinkles
5   0001  donut  Cake  0.55              1001             Regular       5003                 Chocolate
6   0001  donut  Cake  0.55              1001             Regular       5004                     Maple
7   0001  donut  Cake  0.55              1002           Chocolate       5001                      None
8   0001  donut  Cake  0.55              1002           Chocolate       5002                    Glazed
9   0001  donut  Cake  0.55              1002           Chocolate       5005                     Sugar
10  0001  donut  Cake  0.55              1002           Chocolate       5007            Powdered Sugar
11  0001  donut  Cake  0.55              1002           Chocolate       5006  Chocolate with Sprinkles
12  0001  donut  Cake  0.55              1002           Chocolate       5003                 Chocolate
13  0001  donut  Cake  0.55              1002           Chocolate       5004                     Maple
14  0001  donut  Cake  0.55              1003           Blueberry       5001                      None
15  0001  donut  Cake  0.55              1003           Blueberry       5002                    Glazed
16  0001  donut  Cake  0.55              1003           Blueberry       5005                     Sugar
17  0001  donut  Cake  0.55              1003           Blueberry       5007            Powdered Sugar
18  0001  donut  Cake  0.55              1003           Blueberry       5006  Chocolate with Sprinkles
19  0001  donut  Cake  0.55              1003           Blueberry       5003                 Chocolate
20  0001  donut  Cake  0.55              1003           Blueberry       5004                     Maple
21  0001  donut  Cake  0.55              1004        Devil's Food       5001                      None
22  0001  donut  Cake  0.55              1004        Devil's Food       5002                    Glazed
23  0001  donut  Cake  0.55              1004        Devil's Food       5005                     Sugar
24  0001  donut  Cake  0.55              1004        Devil's Food       5007            Powdered Sugar
25  0001  donut  Cake  0.55              1004        Devil's Food       5006  Chocolate with Sprinkles
26  0001  donut  Cake  0.55              1004        Devil's Food       5003                 Chocolate
27  0001  donut  Cake  0.55              1004        Devil's Food       5004                     Maple

根据上面的内容,好吧,交叉连接函数的作用与数据帧解决方案几乎相同,但没有数据帧,因此速度更快。

我添加了展平列表生成器,因为我想确保 JSON 数组都很好并且扁平化,然后作为单个字典列表提供,其中包含分配给每个列表的值之前的一次迭代中的前一个键。这几乎模仿了pandas.concat在这种情况下的行为。

main函数中的逻辑,json_to_dataframe然后就和以前一样了。需要改变的只是将数据帧作为编码函数执行的操作。

另外,在数据帧解决方案中,我没有将前一个标题附加到嵌套对象中,但除非您 100% 确定列名称中没有冲突,否则它几乎是强制性的。

我希望这有帮助 :)。

EDIT: 修改了交叉连接函数来处理嵌套列表为空的情况,基本上保持先前的结果集不变。即使在示例 JSON 数据中添加空 JSON 列表后,输出也不会更改。谢谢你, @纳兹穆斯·萨基布指出这一点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Python 中将嵌套 JSON 转换为 CSV 文件 的相关文章

随机推荐