Pandas: Как сгладить списки диктов в списке диктов в кадре данных, выдавая ошибку, если в каком-либо диктанте во вложенном списке отсутствуют какие-либо указанные ключи? - PullRequest
2 голосов
/ 22 февраля 2020

Моя конечная цель состоит в том, чтобы сгладить ключ в списке диктов в кадре данных. Значение ключа также является списком диктов, и этот список может быть пустым для любой данной записи в списке верхнего уровня. Я пытаюсь сделать это быстро, поэтому пытаюсь использовать векторизованные операции в pandas, включая json_normalize (что, я полагаю, лучше, чем al oop).

В результирующем фрейме данных я хочу сохранить некоторые столбцы верхнего уровня, сглаживая все ключи в списке вложенных диктов. Я также хочу, чтобы операция завершилась неудачей, если в любом вложенном списке есть какой-то элемент dict, в котором нет всех указанных мной ключей. Но я не хочу, чтобы операция не выполнялась, если ключ существует, но имеет значение None. (Вот почему я не могу просто проверить NaN после нормализации - None преобразуется в NaN для типов с плавающей запятой в json_normalize, поскольку он не предоставляет аргумент dtype, поэтому я бы не стал Не знаю, было ли это NaN, потому что ключ не существовал или потому что он действительно существовал, но был None).

Например, я попытался сделать что-то вроде этого:

data = [
  {
    'id': 1, 
    'topfield1': "1-1",
    'topfield2': "1-2",
    'topfield3': "1-3",
    'topfield4': "1-4",
    'payments': [
      {'id': 1, 'amt': 2.0, 'not_required': 'something'},
      {'id': 2, 'amt': 4.0}
    ]
  },
  {
    'id': 2, 
    'topfield1': "2-1",
    'topfield2': "2-2",
    'topfield3': "2-3",
    'topfield4': "2-4",
    'payments': [
      {'id': 1}
    ]
  },
  {
    'id': 3, 
    'topfield1': "3-1",
    'topfield2': "3-2",
    'topfield3': "3-3",
    'topfield4': "3-4",
    'payments': []
  }
]

# now flatten into one row for each item in each record's 'payments' key, keeping top-level 'id', 'topfield1', 'topfield4' and raising error if payments.id or payments.amt does not exist in a payment

#ideally, it would work like this:

# i want this to raise an error since data[1]['payments'][0] does not have key 'amt'. apparently that's not how json_normalize works -- it just throws a TypeError because apparently meta columns can't be from the record_path
pandas.io.json.json_normalize(data, record_path='payments', meta=['id', 'topfield4', 'topfield1', ['payments', 'id'], ['payments', 'amt']]) 
'''expected output
KeyError: 'payments.amt' or something like that
'''
'''actual output
TypeError: list indices must be integers or slices, not str
'''

data[1]['payments'][0]['amt'] = None
# now every 'payment' has 'id' and 'amt' keys so should succeed. but this still throws TypeError.
pandas.io.json.json_normalize(data, record_path='payments', meta=['id', 'topfield4', 'topfield1', ['payments', 'id'], ['payments', 'amt']]) 
'''expected output
id  topfield4  topfield1  payments.id  payments.amt  payments.not_required
1      1-4     1-1             1            2.0            something
1      1-4     1-1             2            4.0              NaN
2      2-4     2-1             1            NaN              NaN
'''
'''actual output
TypeError: list indices must be integers or slices, not str
'''

Но это не работает. Всякий раз, когда я использую поля из payments объектов - то есть ключей объектов в списке record_path - в качестве полей в meta arg, я получаю TypeError: list indices must be integers or slices, not str.

Это также выглядит как json_normalize не достаточно умен, чтобы фактически следовать тем путям, которые вы указали в аргументе meta, поскольку: (хотя я думаю, я мог бы просто переименовать соответствующие столбцы верхнего уровня, чтобы избежать)

# data as from above
pandas.io.json.json_normalize(data, record_path='payments', meta=['id'])
# fails with ValueError: Conflicting metadata name id, need distinguishing prefix`
# (shouldn't it know `id` is top-level since it's not a nested path?)

Есть ли векторизованный / быстрый способ выполнения sh того, что я хочу сделать?

Спасибо за любую помощь.

РЕДАКТИРОВАТЬ 1: может быть много полей верхнего уровня, и я хочу их подмножество в окончательном фрейме данных. РЕДАКТИРОВАТЬ 2: Добавлено больше полей, чтобы сделать минимально воспроизводимый пример.

1 Ответ

0 голосов
/ 22 февраля 2020

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

df = pd.concat(
    {d["id"]: pd.DataFrame.from_dict(d["payments"]) for d in data}, sort=False, axis=0
).reset_index(0).rename(columns={"id": "payments.id", "level_0": "id"})

print(df)

   id  payments.id  amt not_required
0   1          1.0  2.0    something
1   1          2.0  4.0          NaN
0   2          1.0  NaN          NaN
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...