Отсутствует первый документ при загрузке многодокументного файла yaml в фрейме данных pandas

Я попытался загрузить файл yaml с несколькими документами (т. Е. Файл yaml, состоящий из нескольких документов yaml, разделенных «---») в фрейм данных Pandas. По какой-то причине первый документ не попадает в фрейм данных. Если вывод yaml.safe_load_all сначала материализуется в список (вместо подачи итератора в pd.io.json.json_normalize), все документы попадают в фрейм данных. Я мог бы воспроизвести это с помощью приведенного ниже примера кода (в совершенно другом файле yaml).

import os
import yaml
import pandas as pd
import urllib.request

# public example of multi-document yaml
inputfilepath = os.path.expanduser("~/my_example.yaml")
url =  "https://raw.githubusercontent.com/kubernetes/examples/master/guestbook/all-in-one/guestbook-all-in-one.yaml"
urllib.request.urlretrieve(url, inputfilepath) 

with open(inputfilepath, 'r') as stream:
     df1 = pd.io.json.json_normalize(yaml.safe_load_all(stream))

with open(inputfilepath, 'r') as stream:
     df2 = pd.io.json.json_normalize([ x for x in yaml.safe_load_all(stream)])

print(f'Output table shape with iterator: {df1.shape}')
print(f'Output table shape with iterator materialized as list: {df2.shape}')

Я ожидаю, что оба результата будут идентичными, но я получаю:

Output table shape with iterator: (5, 18)
Output table shape with iterator materialized as list: (6, 18)

Любые идеи, почему эти результаты отличаются?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
227
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

См. этот сайт для понимание списка против выражений генератора.

df1 отсутствует первая строка данных, потому что вы передаете итератор вместо повторяемый.

print(yaml.safe_load_all(stream))
#Output: <generator object load_all at 0x00000293E1697750>

От документы панды он ожидает список:

data : dict or list of dicts

Обновление для более подробной информации:

При просмотре исходного файла normalize.py функция json_normalize имеет эту условную проверку, которая делает так, что ваш генератор обрабатывается так, как вы передали во вложенной структуре:

if any([isinstance(x, dict)
    for x in compat.itervalues(y)] for y in data):
        # naive normalization, this is idempotent for flat records
        # and potentially will inflate the data considerably for
        # deeply nested structures:
        #  {VeryLong: { b: 1,c:2}} -> {VeryLong.b:1 ,VeryLong.c:@}
        #
        # TODO: handle record value which are lists, at least error
        #       reasonably
        data = nested_to_record(data, sep=sep)
    return DataFrame(data)

Внутри функции nested_to_record:

new_d = copy.deepcopy(d)
for k, v in d.items():
    # each key gets renamed with prefix
    if not isinstance(k, compat.string_types):
        k = str(k)
    if level == 0:
        newkey = k
    else:
        newkey = prefix + sep + k

    # only dicts gets recurse-flattend
    # only at level>1 do we rename the rest of the keys
    if not isinstance(v, dict):
        if level != 0:  # so we skip copying for top level, common case
            v = new_d.pop(k)
            new_d[newkey] = v
        continue
    else:
        v = new_d.pop(k)
        new_d.update(nested_to_record(v, newkey, sep, level + 1))
new_ds.append(new_d)

В строке d.items() оценивается ваш генератор, а внутри цикла вы можете видеть, что они пропускают первый «уровень», который в вашем случае является первой записью.

Спасибо за ваше объяснение и за изучение этого! Я думаю, что это должно быть поймано пандами: github.com/pandas-dev/pandas/pull/26646

Marvin Steijaert 04.06.2019 16:50

Другие вопросы по теме