нормализуйте json, но сохраняйте идентификатор с верхнего уровня —

#python #json #pandas

Вопрос:

У меня есть файл json из ответа API, который в целом соответствует следующей схеме

 import pandas as pd 


j =[
  {
    "orders": [
      {
        "orderId": 0,
        "items": [
          { "item_1": "x", "item_price": 5.99 },
          { "item_1": "y", "item_price": 15.99 }
        ]
      }
    ]
  }
]
 

То, что я пытаюсь сделать,-это нормализовать json, но сохранить идентификатор заказа с верхнего уровня, чтобы я мог повторно присоединиться к наборам данных дальше по конвейеру данных.

Я думал, что это можно сделать с помощью одного из аргументов ключевого слова в pd.json_normalize

 df_orders = pd.json_normalize(j,record_path=['orders'])

print(df_orders)

   orderId                                              items
0        0  [{'item_1': 'x', 'item_price': 5.99}, {'item_1...
 

 df_items = pd.json_normalize(j,record_path=['orders','items'])
print(df_items)

  item_1  item_price
0      x        5.99
1      y       15.99
 

Чего бы я хотел, так это

   item_1  item_price  orders.orderId
0      x        5.99  0
1      y       15.99  0 
 

Я просмотрел документацию здесь

и попытался использовать

 pd.json_normalize(j,record_path=['orders','items'], meta=['orders'])
 

который просто добавляет весь объект json к каждой записи.

   item_1  item_price                                             orders
0      x        5.99  {'orderId': 0, 'items': [{'item_1': 'x', 'item...
1      y       15.99  {'orderId': 0, 'items': [{'item_1': 'x', 'item...
 

какие-нибудь советы по использованию pd.json_normalize ?

У меня нет проблем с разбором json вручную и выполнением этого в 2-х шаговом процессе, но я хочу избежать дальнейших сбоев в своем конвейере, чтобы сохранить динамику, если я могу помочь.

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

1. если вам не нужны какие-либо зависимости, вы можете попробовать следующее : pd.json_normalize(j,record_path=['orders','items'], meta=['orders']).assign(orderId = lambda df: df.orders.str.get('orderId')).drop(columns='orders') . если вы не против jmespath, это может быть полезно здесь

2. не уверен, что вы подразумеваете под выравниванием. приведенный выше код должен работать, так как Панды выравниваются по индексу перед вычислением какой-либо операции

Ответ №1:

Вы можете попробовать поставить дополнительную пару квадратных скобок в meta параметре и указать upto orderId .

 pd.json_normalize(j,record_path=['orders','items'], meta=[['orders', 'orderId']])
 

Результат:

   item_1  item_price orders.orderId
0      x        5.99              0
1      y       15.99              0
 

Синтаксис meta in pd.json_normalize заключается в том, что всякий раз, когда вы хотите получить доступ к полю второго уровня ниже верхнего уровня, вы должны указать его в списке второго уровня. Список на втором уровне в этом случае, т. е. ['orders', 'orderId'] действует как путь. Мы все еще не можем пропустить указание списка верхнего уровня, даже если у нас есть 2 элемента в этом списке второго уровня. Для справки мы можем посмотреть примеры в официальном документе и посмотреть, например, случай, когда мы хотим получить доступ к полю governor в примере.

Поле orderId рассматривается как поле второго уровня, а не верхнего уровня, вероятно, из-за того, что поле orders рассматривается как верхний уровень, что, в свою очередь, связано с тем, что именно данные j мы передаем в pd.json_normalize качестве входных данных и pd.json_normalize подсчитываем уровни на основе этих входных данных j . Даже если мы уточним record_path , эти относительные уровни все равно остаются прежними.

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

1. красиво сработало идеально. зачем нужен список списков?

2. @Умар. H Глядя на пример официального документа , вы можете видеть, что нам нужно включить в список список, чтобы получить поле governor в примере.

3. не могу поверить, что я это пропустил! еще раз спасибо!

4. @Умар. Х Да, я тоже не могу в это поверить. Вы решили много подобных проблем для других. Наверное, мы иногда слишком концентрируемся на проблеме и путаемся. 🙂

5. ваши корректировки фантастичны, приятель, вы должны сделать пиар, сделать документы и улучшить их 🙂

Ответ №2:

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

Это работает:

 pd.json_normalize(j,record_path=['orders','items'],meta[['orders','orderId']])
 
   item_1  item_price orders.orderId
0      x        5.99              0
1      y       15.99              0
 

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

1. это то же самое, что и ответ выше?

2. Да! Здесь нет ничего дополнительного. Я думаю, что я не обновил страницу перед публикацией своего ответа или, по крайней мере, не видел другого ответа. Рад, что с тобой разобрались!