【问题标题】:Convert JSON to CSV with complex arrays in Python在 Python 中使用复杂数组将 JSON 转换为 CSV
【发布时间】:2021-03-31 15:46:16
【问题描述】:

我有几个带有嵌套数据的 JSON 文件。利用 Python,我可以使用 pandas 来帮助解决这个问题:

import pandas as pd

df = pd.read_json (r'data.json')
export_csv = df.to_csv (r'data.csv', index = None, header=True)

但是,这只适用于简单的 JSON 文件。我拥有的那些嵌套数组很复杂,并且一些 JSON 数据合并在列下。例如,如果我们要使用这个示例数据:

data.json

[
  {
    "id": 1,
    "name": {
      "english": "Bulbasaur",
      "french": "Bulbizarre"
    },
    "type": [
      "Grass",
      "Poison"
    ],
    "base": {
      "HP": 45,
      "Attack": 49,
      "Defense": 49
    }
  },
  {
    "id": 2,
    "name": {
      "english": "Ivysaur",
      "french": "Herbizarre"
    },
    "type": [
      "Grass",
      "Poison"
    ],
    "base": {
      "HP": 60,
      "Attack": 62,
      "Defense": 63
    }
  }
]

结果如下:

您可以看到超过第一级的任何数组都以 JSON 格式显示(例如{'english': 'Bulbasaur', 'french': 'Bulbizarre'})。理想情况下,它应该将这些子数组分解为具有元素名称的列:

最重要的是,其他 JSON 文件具有不同的元素名称和顺序。因此,脚本应该捕获所有不同的元素名称,然后将它们转换为 CSV 列。

我怎样才能做到这一点?

【问题讨论】:

  • 这些列的标准如何?您是否知道它总是,例如“name.french”和“name.engilsh”,或者随着时间的推移可能会出现任何形式的名称类型?这意味着动态添加列并让预先存在的行将 NONE 放入这些单元格中。
  • @tdelaney:不幸的是,并非所有的元素名称或顺序都相同。因此,这将检查 JSON 中的所有元素名称并将它们转换为列。

标签: python json pandas csv export-to-csv


【解决方案1】:

查看 flatten_json

from flatten_json import flatten
dic = [
  {
    "id": 1,
    "name": {
      "english": "Bulbasaur",
      "french": "Bulbizarre"
    },
    "type": [
      "Grass",
      "Poison"
    ],
    "base": {
      "HP": 45,
      "Attack": 49,
      "Defense": 49
    }
  },
  {
    "id": 2,
    "name": {
      "english": "Ivysaur",
      "french": "Herbizarre"
    },
    "type": [
      "Grass",
      "Poison"
    ],
    "base": {
      "HP": 60,
      "Attack": 62,
      "Defense": 63
    }
  }
]

dic_flattened = (flatten(d, '.') for d in dic)
df = pd.DataFrame(dic_flattened)

输出:

   id name.english name.french type.0  type.1  base.HP  base.Attack  base.Defense
0   1    Bulbasaur  Bulbizarre  Grass  Poison       45           49            49
1   2      Ivysaur  Herbizarre  Grass  Poison       60           62            63

【讨论】:

    【解决方案2】:

    我建议使用 for 循环和 defaultdict,在进行迭代(没有聚合)时通常更容易和更快,以在最终输出之前远离 pandas:

    from collections import defaultdict
    
    df = defaultdict(list)
    
    val = {}
    box = []
    for entry in data: # data is the sample data you shared
        for key, value in entry.items():
            if key == "id":
                temp = [(key, value)]
            elif isinstance(value, dict):
                temp = [(f"{key}.{k}", v) for k, v in value.items()]
            else:
                temp = [(f"{key}.{k}", v) for k, v in enumerate(value, 1)]
            box.extend(temp)
    
    for k, v in box:
        df[k].append(v)
    
    
    df
    
    defaultdict(list,
                {'id': [1, 2],
                 'name.english': ['Bulbasaur', 'Ivysaur'],
                 'name.french': ['Bulbizarre', 'Herbizarre'],
                 'type.1': ['Grass', 'Grass'],
                 'type.2': ['Poison', 'Poison'],
                 'base.HP': [45, 60],
                 'base.Attack': [49, 62],
                 'base.Defense': [49, 63]})
    

    创建数据框

    pd.DataFrame(df)
    
        id  name.english    name.french type.1  type.2  base.HP base.Attack base.Defense
    0   1   Bulbasaur      Bulbizarre   Grass   Poison     45      49       49
    1   2   Ivysaur        Herbizarre   Grass   Poison     60      62       63
    

    【讨论】:

    • 感谢您的新方法。从您的代码中,看起来字段名称是在底部的defaultdict 中预定义的?我应该澄清一下,其他 JSON 文件具有不同的元素名称和顺序。所以我希望它能够捕获所有可能的元素名称,然后将其转换为 CSV。更新了我的问题以澄清。
    • “看起来字段名是在底部的defaultdict中预定义的?” 我觉得不是预定义,我觉得是程序的输出如果您打印df。您不应该在脚本中包含该部分。在任何情况下,在程序结束时进行预定义都没有多大意义。
    【解决方案3】:

    使用json_normalize 将让您几乎到达那里,但要拆分列表,您需要额外的东西:

    f = lambda x: 'type.{}'.format(x + 1)
    df = df.join(pd.DataFrame(df.pop('type').values.tolist()).rename(columns=f))
    
    print(df)
    

    输出

       id name.english name.french  ...  base.Defense  type.1  type.2
    0   1    Bulbasaur  Bulbizarre  ...            49   Grass  Poison
    1   2      Ivysaur  Herbizarre  ...            63   Grass  Poison
    
    [2 rows x 8 columns]
    

    【讨论】:

    • 谢谢,但看起来您正在指定要拆分的 type 元素。我应该澄清的是,并非所有 JSON 文件都具有相同的元素名称或顺序。所以它需要捕获所有 JSON文件中的元素名称,然后将它们转换为CSV列。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-01
    • 2017-11-17
    • 2020-06-10
    • 2021-10-29
    • 2018-08-14
    • 1970-01-01
    相关资源
    最近更新 更多