Запись Вложенного Списка Словарей JSON В CSV

#json #python-3.x #csv

Вопрос:

Вопрос

Я пытаюсь написать следующий вложенный список словаря, в котором есть еще один список словаря в csv. Я перепробовал несколько способов, но не могу заставить его правильно написать:

Данные Json

 [
    {
        "Basic_Information_Source": [
            {
                "Image": "image1.png",
                "Image_Format": "PNG",
                "Image_Mode": "RGB",
                "Image_Width": 574,
                "Image_Height": 262,
                "Image_Size": 277274
            }
        ],
        "Basic_Information_Destination": [
            {
                "Image": "image1_dst.png",
                "Image_Format": "PNG",
                "Image_Mode": "RGB",
                "Image_Width": 574,
                "Image_Height": 262,
                "Image_Size": 277539
            }
        ],
        "Values": [
            {
                "Value1": 75.05045463635267,
                "Value2": 0.006097560975609756,
                "Value3": 0.045083481733371615,
                "Value4": 0.008639858263904898
            }
        ]
    },
    {
        "Basic_Information_Source": [
            {
                "Image": "image2.png",
                "Image_Format": "PNG",
                "Image_Mode": "RGB",
                "Image_Width": 1600,
                "Image_Height": 1066,
                "Image_Size": 1786254
            }
        ],
        "Basic_Information_Destination": [
            {
                "Image": "image2_dst.png",
                "Image_Format": "PNG",
                "Image_Mode": "RGB",
                "Image_Width": 1600,
                "Image_Height": 1066,
                "Image_Size": 1782197
            }
        ],
        "Values": [
            {
                "Value1": 85.52662890580055,
                "Value2": 0.0005464352720450282,
                "Value3": 0.013496113910369758,
                "Value4": 0.003800236380811839
            }
        ]
    }
]
 

Рабочий Код

Я попытался использовать следующий код, и он работает, но он сохранил только заголовки, а затем выгрузил весь базовый список в виде текста в csv-файле:

 import json
import csv

def Convert_CSV():

    ar_enc_file = open('analysis_results_enc.json','r')
    json_data = json.load(ar_enc_file)

    keys = json_data[0].keys()
    
    with open('test.csv', 'w', encoding='utf8', newline='')  as output_file:
        dict_writer = csv.DictWriter(output_file, keys)
        dict_writer.writeheader()
        dict_writer.writerows(json_data)

    ar_enc_file.close()

Convert_CSV()
 

Рабочий вывод / Проблема с ним

На выходе записывается следующий заголовок:

  • basic_информация_источник
  • basic_информация_дестинация
  • Ценности

А затем он сбрасывает все остальные данные внутри каждого заголовка в виде списка, подобного этому:

 [{'Image': 'image1.png', 'Image_Format': 'PNG', 'Image_Mode': 'RGB', 'Image_Width': 574, 'Image_Height': 262, 'Image_Size': 277274}]
 

Ожидаемый результат / Образец

Ожидаемый Результат

Попытка сгенерировать вышеуказанный тип вывода для каждого словаря в массиве словарей.

Как это правильно написать?

Комментарии:

1. Каков ваш предпочтительный результат?

2. Вы хотели полностью выровнять вывод? Если да, то как вы хотите представить вложенные списки? Нам действительно нужно увидеть результат, на который вы надеетесь, чтобы помочь.

3. @Axe319 Спасибо, я добавил ожидаемый формат вывода. Я пытаюсь создать подобный формат для каждого диктатора в массиве.

4. @JonSG Спасибо, я добавил ожидаемый формат вывода. Я пытаюсь создать подобный формат для каждого диктатора в массиве.

Ответ №1:

Я уверен, что кто — нибудь придет с гораздо более элегантным решением. Это, как говорится:

У вас есть несколько проблем.

  • У вас есть несогласованные записи с полями, которые вы хотите выровнять.
  • Даже если вы заполняете свои данные, у вас есть промежуточные list значения, которые необходимо выровнять.
  • Тогда у вас все еще есть отдельные данные, которые необходимо объединить вместе.
  • DictWriter AFAIK ожидает, что это данные в формате [{'column': 'entry'},{'column': 'entry'} , поэтому, даже если вы выполните все предыдущие шаги, вы все равно не в правильном формате.

Так что давайте начнем.

Для первых двух частей мы можем объединить.

 def pad_list(lst, size, padding=None):
    # we wouldn't have to make a copy but I prefer to
    # avoid the possibility of getting bitten by mutability
    _lst = lst[:]
    for _ in range(len(lst), size):
        _lst.append(padding)
    return _lst


# this expects already parsed json data
def flatten(json_data):
    lst = []
    for dct in json_data:
        # here we're just setting a max size of all dict entries
        # this is in case the shorter entry is in the first iteration
        max_size = 0
        # we initialize a dict for each of the list entries
        # this is in case you have inconsistent lengths between lists
        flattened = dict()
        for k, v in dct.items():
            entries = list(next(iter(v), dict()).values())
            flattened[k] = entries
            max_size = max(len(entries), max_size)
        # here we append the padded version of the keys for the dict
        lst.append({k: pad_list(v, max_size) for k, v in flattened.items()})
    return lst
 

Итак, теперь у нас есть сплющенный список dict s, значения list которых имеют одинаковую длину. По существу:

 [
    {
        "Basic_Information_Source": [
            "image1.png",
            "PNG",
            "RGB",
            574,
            262,
            277274
        ],
        "Basic_Information_Destination": [
            "image1_dst.png",
            "PNG",
            "RGB",
            574,
            262,
            277539
        ],
        "Values": [
            75.05045463635267,
            0.006097560975609756,
            0.045083481733371615,
            0.008639858263904898,
            None,
            None
        ]
    }
]
 

Но у этого list есть несколько dict s, которые нужно объединить, а не только один.

Поэтому нам нужно объединиться.

 # this should be self explanatory
def merge(flattened):
    merged = dict()
    for dct in flattened:
        for k, v in dct.items():
            if k not in merged:
                merged[k] = []
            merged[k].extend(v)
    return merged
 

Это дает нам что-то близкое к этому:

 {
    "Basic_Information_Source": [
        "image1.png",
        "PNG",
        "RGB",
        574,
        262,
        277274,
        "image2.png",
        "PNG",
        "RGB",
        1600,
        1066,
        1786254
    ],
    "Basic_Information_Destination": [
        "image1_dst.png",
        "PNG",
        "RGB",
        574,
        262,
        277539,
        "image2_dst.png",
        "PNG",
        "RGB",
        1600,
        1066,
        1782197
    ],
    "Values": [
        75.05045463635267,
        0.006097560975609756,
        0.045083481733371615,
        0.008639858263904898,
        None,
        None,
        85.52662890580055,
        0.0005464352720450282,
        0.013496113910369758,
        0.003800236380811839,
        None,
        None
    ]
}
 

Но подождите, нам все равно нужно отформатировать его для автора.

Наши данные должны быть в формате [{'column_1': 'entry', column_2: 'entry'},{'column_1': 'entry', column_2: 'entry'}

Поэтому мы форматируем:

 def format_for_writer(merged):
    formatted = []
    for k, v in merged.items():
        for i, item in enumerate(v):
            # on the first pass this will append an empty dict
            # on subsequent passes it will be ignored
            # and add keys into the existing dict
            if i >= len(formatted):
                formatted.append(dict())
            formatted[i][k] = item
    return formatted
 

Итак, наконец, у нас есть хорошая чистая отформатированная структура данных, которую мы можем просто передать нашей функции записи.

 def convert_csv(formatted):
    keys = formatted[0].keys()
    with open('test.csv', 'w', encoding='utf8', newline='')  as output_file:
        dict_writer = csv.DictWriter(output_file, keys)
        dict_writer.writeheader()
        dict_writer.writerows(formatted)
 

Полный код со json строкой:

 import json
import csv

json_raw = """
[
    {
        "Basic_Information_Source": [
            {
                "Image": "image1.png",
                "Image_Format": "PNG",
                "Image_Mode": "RGB",
                "Image_Width": 574,
                "Image_Height": 262,
                "Image_Size": 277274
            }
        ],
        "Basic_Information_Destination": [
            {
                "Image": "image1_dst.png",
                "Image_Format": "PNG",
                "Image_Mode": "RGB",
                "Image_Width": 574,
                "Image_Height": 262,
                "Image_Size": 277539
            }
        ],
        "Values": [
            {
                "Value1": 75.05045463635267,
                "Value2": 0.006097560975609756,
                "Value3": 0.045083481733371615,
                "Value4": 0.008639858263904898
            }
        ]
    },
    {
        "Basic_Information_Source": [
            {
                "Image": "image2.png",
                "Image_Format": "PNG",
                "Image_Mode": "RGB",
                "Image_Width": 1600,
                "Image_Height": 1066,
                "Image_Size": 1786254
            }
        ],
        "Basic_Information_Destination": [
            {
                "Image": "image2_dst.png",
                "Image_Format": "PNG",
                "Image_Mode": "RGB",
                "Image_Width": 1600,
                "Image_Height": 1066,
                "Image_Size": 1782197
            }
        ],
        "Values": [
            {
                "Value1": 85.52662890580055,
                "Value2": 0.0005464352720450282,
                "Value3": 0.013496113910369758,
                "Value4": 0.003800236380811839
            }
        ]
    }
]
"""


def pad_list(lst, size, padding=None):
    _lst = lst[:]
    for _ in range(len(lst), size):
        _lst.append(padding)
    return _lst


def flatten(json_data):
    lst = []
    for dct in json_data:
        max_size = 0
        flattened = dict()
        for k, v in dct.items():
            entries = list(next(iter(v), dict()).values())
            flattened[k] = entries
            max_size = max(len(entries), max_size)
        lst.append({k: pad_list(v, max_size) for k, v in flattened.items()})
    return lst


def merge(flattened):
    merged = dict()
    for dct in flattened:
        for k, v in dct.items():
            if k not in merged:
                merged[k] = []
            merged[k].extend(v)
    return merged


def format_for_writer(merged):
    formatted = []
    for k, v in merged.items():
        for i, item in enumerate(v):
            if i >= len(formatted):
                formatted.append(dict())
            formatted[i][k] = item
    return formatted


def convert_csv(formatted):
    keys = formatted[0].keys()
    with open('test.csv', 'w', encoding='utf8', newline='')  as output_file:
        dict_writer = csv.DictWriter(output_file, keys)
        dict_writer.writeheader()
        dict_writer.writerows(formatted)


def main():
    json_data = json.loads(json_raw)
    flattened = flatten(json_data)
    merged = merge(flattened)
    formatted = format_for_writer(merged)
    convert_csv(formatted)


if __name__ == '__main__':
    main()